// pkg.v — `vr init` / `vr get` / `vr install`: a minimal package manager. // // Packages are fetched into ./vendor// and recorded in vr.mod as // `dep ""`. The compiler resolves `import "pkg/file"` against vendor/ // automatically (see compiler/compiler.v). Supported package specs: // // vr get owner/repo clone from GitHub (https://github.com/owner/repo) // vr get https://host/repo clone any git repository // vr get ./local/path copy a local directory module main import os const manifest_file = 'vr.mod' const vendor_dir = 'vendor' fn toolchain_init(args []string) ! { pkg := if args.len > 0 { args[0] } else { os.base(os.getwd()) } if os.exists(manifest_file) { return error('${manifest_file} already exists here') } os.write_file(manifest_file, 'module ${pkg}\nversion 0.1.0\n\n# deps:\n# dep "owner/repo"\n')! os.mkdir('vendor') or {} if !os.exists('main.vr') { os.write_file('main.vr', 'fn main() {\n\tprintln("hello from ${pkg}")\n}\n')! } if !os.exists('build.vrmm') { os.write_file('build.vrmm', '// build.vrmm — build instructions for the VuurRaaf toolchain. // // vr make runs main() // vr make clean runs the clean() target // // Build builtins: build_compile, build_assemble, build_link, build_run, // build_test, build_bench, build_clean, build_exec, build_exec_status, // build_exists, build_mkdir, build_rm, build_copy, build_glob, build_ls, // build_base, build_dir, build_join, build_root. fn main() { build_compile("main.vr", "main.vobj") build_link(["main.vobj"], "main.vbin") println("built main.vbin — run it with: vr run main.vbin") } fn clean() { build_clean() } ')! } println('initialized project "${pkg}" (${manifest_file}, main.vr, build.vrmm)') } fn toolchain_get(args []string) ! { if args.len == 0 { return error('usage: vr get ') } spec := args[0] // determine the package name and how to fetch it mut pkg := '' if spec.starts_with('./') || spec.starts_with('/') { pkg = os.base(spec) } else if spec.contains('://') { pkg = os.base(spec) } else if spec.contains('/') { pkg = spec.all_after('/') } else { pkg = spec } if pkg.ends_with('.git') { pkg = pkg[..pkg.len - 4] } if pkg == '' || pkg == '.' { return error('cannot derive a package name from "${spec}"') } os.mkdir(vendor_dir) or {} dst := os.join_path(vendor_dir, pkg) if os.exists(dst) { return error('package "${pkg}" already exists at ${dst}') } if spec.starts_with('./') || spec.starts_with('/') { copy_dir(spec, dst) or { return error('cannot copy ${spec}: ${err.msg()}') } } else { url := if spec.contains('://') { spec } else { 'https://github.com/${spec}.git' } println('cloning ${url} -> ${dst} ...') r := os.exec(['git', 'clone', '--depth', '1', url, dst]) if r.exit_code != 0 { return error('git clone failed: ${r.output}') } } // record the dependency in the manifest (idempotent) if os.exists(manifest_file) { manifest := os.read_file(manifest_file) or { '' } if !manifest.contains('dep "${spec}"') { os.write_file(manifest_file, manifest.trim_space() + '\ndep "${spec}"\n') or { return error('cannot update ${manifest_file}: ${err.msg()}') } } } println('added package "${pkg}" (${spec})') } fn toolchain_install() ! { if !os.exists(manifest_file) { return error('no ${manifest_file} found — run `vr init` first') } manifest := os.read_file(manifest_file)! mut n := 0 for line in manifest.split_into_lines() { t := line.trim_space() if !t.starts_with('dep "') { continue } mut spec := t[5..] if spec.ends_with('"') { spec = spec[..spec.len - 1] } if spec == '' { continue } n++ toolchain_get([spec]) or { eprintln(' install: ${err.msg()}') } } if n == 0 { println('no dependencies in ${manifest_file}') return } println('installed ${n} package(s) into ${vendor_dir}/') } // copy_dir recursively copies a local directory tree. fn copy_dir(src string, dst string) ! { if !os.is_dir(src) { return error('${src} is not a directory') } os.mkdir_all(dst) or { return error('cannot create ${dst}: ${err.msg()}') } for entry in os.ls(src) or { return error('cannot read ${src}: ${err.msg()}') } { sp := os.join_path(src, entry) dp := os.join_path(dst, entry) if os.is_dir(sp) { copy_dir(sp, dp)! } else if os.is_file(sp) { os.cp(sp, dp, os.CopyParams{}) or { return error('cannot copy ${sp}: ${err.msg()}') } } } } fn toolchain_list() ! { if !os.exists(manifest_file) { return error('no ${manifest_file} found here') } manifest := os.read_file(manifest_file)! mod_name := manifest.all_before('\n').replace('module ', '').trim_space() println('project: ${mod_name}') mut found := false for line in manifest.split_into_lines() { t := line.trim_space() if t.starts_with('dep "') { found = true mut spec := t[5..] if spec.ends_with('"') { spec = spec[..spec.len - 1] } mut pkg := os.base(spec) if pkg.ends_with('.git') { pkg = pkg[..pkg.len - 4] } status := if os.exists(os.join_path(vendor_dir, pkg)) { 'installed' } else { 'missing' } println(' ${pkg} (${status}) <- ${spec}') } } if !found { println(' (no dependencies)') } }