mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
251 lines
4.8 KiB
V
251 lines
4.8 KiB
V
|
module pkgconfig
|
||
|
|
||
|
import semver
|
||
|
import os
|
||
|
|
||
|
const (
|
||
|
default_paths = [
|
||
|
'/usr/local/lib/x86_64-linux-gnu/pkgconfig',
|
||
|
'/usr/local/lib/pkgconfig',
|
||
|
'/usr/local/share/pkgconfig',
|
||
|
'/usr/lib/x86_64-linux-gnu/pkgconfig',
|
||
|
'/usr/lib/pkgconfig',
|
||
|
'/usr/share/pkgconfig',
|
||
|
]
|
||
|
version = '0.2.0'
|
||
|
)
|
||
|
|
||
|
pub struct Options {
|
||
|
pub:
|
||
|
path string
|
||
|
debug bool
|
||
|
norecurse bool
|
||
|
}
|
||
|
|
||
|
pub struct PkgConfig {
|
||
|
pub mut:
|
||
|
options Options
|
||
|
libs []string
|
||
|
libs_private []string
|
||
|
cflags []string
|
||
|
paths []string // TODO: move to options?
|
||
|
vars map[string]string
|
||
|
requires []string
|
||
|
version string
|
||
|
description string
|
||
|
name string
|
||
|
modname string
|
||
|
}
|
||
|
|
||
|
fn (mut pc PkgConfig) filters(s string) []string {
|
||
|
r := pc.filter(s).split(' ')
|
||
|
mut res := []string{}
|
||
|
for a in r {
|
||
|
if a != '' {
|
||
|
res << a
|
||
|
}
|
||
|
}
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
fn (mut pc PkgConfig) filter(s string) string {
|
||
|
mut r := s.trim_space()
|
||
|
for r.contains('\${') {
|
||
|
tok0 := r.index('\${') or {
|
||
|
break
|
||
|
}
|
||
|
mut tok1 := r[tok0..].index('}') or {
|
||
|
break
|
||
|
}
|
||
|
tok1 += tok0
|
||
|
v := r[tok0 + 2..tok1]
|
||
|
r = r.replace('\${$v}', pc.vars[v])
|
||
|
}
|
||
|
return r.trim_space()
|
||
|
}
|
||
|
|
||
|
fn (mut pc PkgConfig) setvar(line string) {
|
||
|
kv := line.trim_space().split('=')
|
||
|
if kv.len == 2 {
|
||
|
k := kv[0]
|
||
|
v := pc.filter(kv[1])
|
||
|
pc.vars[k] = pc.filter(v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn (mut pc PkgConfig) parse(file string) bool {
|
||
|
data := os.read_file(file) or {
|
||
|
return false
|
||
|
}
|
||
|
if pc.options.debug {
|
||
|
eprintln(data)
|
||
|
}
|
||
|
lines := data.split('\n')
|
||
|
if pc.options.norecurse {
|
||
|
// 2x faster than original pkg-config for --list-all --description
|
||
|
// TODO: use different variable. norecurse have nothing to do with this
|
||
|
for line in lines {
|
||
|
if line.starts_with('Description: ') {
|
||
|
pc.description = pc.filter(line[13..])
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
for line in lines {
|
||
|
if line.starts_with('#') {
|
||
|
continue
|
||
|
}
|
||
|
if line.contains('=') && !line.contains(' ') {
|
||
|
pc.setvar(line)
|
||
|
continue
|
||
|
}
|
||
|
if line.starts_with('Description: ') {
|
||
|
pc.description = pc.filter(line[13..])
|
||
|
} else if line.starts_with('Name: ') {
|
||
|
pc.name = pc.filter(line[6..])
|
||
|
} else if line.starts_with('Version: ') {
|
||
|
pc.version = pc.filter(line[9..])
|
||
|
} else if line.starts_with('Requires: ') {
|
||
|
pc.requires = pc.filters(line[10..])
|
||
|
} else if line.starts_with('Cflags: ') {
|
||
|
pc.cflags = pc.filters(line[8..])
|
||
|
} else if line.starts_with('Libs: ') {
|
||
|
pc.libs = pc.filters(line[6..])
|
||
|
} else if line.starts_with('Libs.private: ') {
|
||
|
pc.libs_private = pc.filters(line[14..])
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
fn (mut pc PkgConfig) resolve(pkgname string) ?string {
|
||
|
if pc.paths.len == 0 {
|
||
|
pc.paths << '.'
|
||
|
}
|
||
|
for path in pc.paths {
|
||
|
file := '$path/${pkgname}.pc'
|
||
|
if os.exists(file) {
|
||
|
return file
|
||
|
}
|
||
|
}
|
||
|
return error('Cannot find "$pkgname" pkgconfig file')
|
||
|
}
|
||
|
|
||
|
pub fn atleast(v string) bool {
|
||
|
v0 := semver.from(version) or {
|
||
|
return false
|
||
|
}
|
||
|
v1 := semver.from(v) or {
|
||
|
return false
|
||
|
}
|
||
|
return v0.gt(v1)
|
||
|
}
|
||
|
|
||
|
pub fn (mut pc PkgConfig) atleast(v string) bool {
|
||
|
v0 := semver.from(pc.version) or {
|
||
|
return false
|
||
|
}
|
||
|
v1 := semver.from(v) or {
|
||
|
return false
|
||
|
}
|
||
|
return v0.gt(v1)
|
||
|
}
|
||
|
|
||
|
pub fn (mut pc PkgConfig) extend(pcdep &PkgConfig) ?string {
|
||
|
for flag in pcdep.cflags {
|
||
|
if pc.cflags.index(flag) == -1 {
|
||
|
pc.cflags << flag
|
||
|
}
|
||
|
}
|
||
|
for lib in pcdep.libs {
|
||
|
if pc.libs.index(lib) == -1 {
|
||
|
pc.libs << lib
|
||
|
}
|
||
|
}
|
||
|
for lib in pcdep.libs_private {
|
||
|
if pc.libs_private.index(lib) == -1 {
|
||
|
pc.libs_private << lib
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn (mut pc PkgConfig) load_requires() {
|
||
|
for dep in pc.requires {
|
||
|
mut pcdep := PkgConfig{
|
||
|
paths: pc.paths
|
||
|
}
|
||
|
depfile := pcdep.resolve(dep) or {
|
||
|
break
|
||
|
}
|
||
|
pcdep.parse(depfile)
|
||
|
pcdep.load_requires()
|
||
|
pc.extend(pcdep)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn (mut pc PkgConfig) add_path(path string) {
|
||
|
p := if path.ends_with('/') { path[0..path.len - 1] } else { path }
|
||
|
if pc.paths.index(p) == -1 {
|
||
|
pc.paths << p
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn (mut pc PkgConfig) load_paths() {
|
||
|
for path in default_paths {
|
||
|
pc.add_path(path)
|
||
|
}
|
||
|
for path in pc.options.path.split(':') {
|
||
|
pc.add_path(path)
|
||
|
}
|
||
|
env_var := os.getenv('PKG_CONFIG_PATH')
|
||
|
if env_var != '' {
|
||
|
env_paths := env_var.trim_space().split(':')
|
||
|
for path in env_paths {
|
||
|
pc.add_path(path)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn load(pkgname string, options Options) ?&PkgConfig {
|
||
|
mut pc := &PkgConfig{
|
||
|
modname: pkgname
|
||
|
options: options
|
||
|
}
|
||
|
pc.load_paths()
|
||
|
file := pc.resolve(pkgname) or {
|
||
|
return error(err)
|
||
|
}
|
||
|
pc.parse(file)
|
||
|
/*
|
||
|
if pc.name != pc.modname {
|
||
|
eprintln('Warning: modname and filename differ $pc.name $pc.modname')
|
||
|
}
|
||
|
*/
|
||
|
if !options.norecurse {
|
||
|
pc.load_requires()
|
||
|
}
|
||
|
return pc
|
||
|
}
|
||
|
|
||
|
pub fn list() []string {
|
||
|
mut pc := &PkgConfig{
|
||
|
options: Options{}
|
||
|
}
|
||
|
pc.load_paths()
|
||
|
mut modules := []string{}
|
||
|
for path in pc.paths {
|
||
|
files := os.ls(path) or {
|
||
|
continue
|
||
|
}
|
||
|
for file in files {
|
||
|
if file.ends_with('.pc') {
|
||
|
name := file.replace('.pc', '')
|
||
|
if modules.index(name) == -1 {
|
||
|
modules << name
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return modules
|
||
|
}
|