Skip to content

Commit

Permalink
vpm.tools: add parse functions to handle module updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ttytm committed Dec 18, 2023
1 parent 136193a commit 54f1f6b
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 47 deletions.
118 changes: 112 additions & 6 deletions cmd/tools/vpm/parse.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import os
import net.http
import v.vmod

struct Module {
struct Parser {
mut:
modules map[string]Module
checked_settings_vcs bool
errors int
}

pub struct Module {
mut:
name string
url string
Expand All @@ -19,11 +26,10 @@ mut:
manifest vmod.Manifest
}

struct Parser {
mut:
modules map[string]Module
checked_settings_vcs bool
errors int
struct InstalledModule {
path string
is_global bool
url string
}

enum ModuleKind {
Expand Down Expand Up @@ -207,6 +213,106 @@ fn (mut m Module) get_installed() {
}
}

fn (mut p Parser) parse_outdated() {
for entry in os.ls(settings.vmodules_path) or { return } {
path := os.join_path(settings.vmodules_path, entry)
if entry in excluded_dirs || !os.is_dir(path) {
continue
}
// Global modules `vmodules_dir/module`.
if os.exists(os.join_path(path, 'v.mod')) {
if !is_outdated(path) {
continue
}
vcs := vcs_used_in_dir(path) or { continue }
args := vcs_info[vcs].args
// TODO: mercurial
url := os.execute_opt('${vcs.str()} ${args.path} ${path} config --get remote.origin.url') or {
vpm_error('failed to get url for `${entry}`.', details: err.msg())
continue
}.output.trim_space()
vpm_log(@FILE_LINE, @FN, 'url: ${url}')
if url.starts_with('https://github.com/vlang')
|| url.starts_with('[email protected]:vlang') {
p.parse_module(entry)
} else {
p.parse_module(url)
}
continue
}
// Modules under publisher namespace `vmodules_dir/publisher/module`.
for mod in os.ls(path) or { continue } {
mod_path := os.join_path(path, mod)
if os.exists(os.join_path(mod_path, 'v.mod')) && is_outdated(mod_path) {
p.parse_module('${entry}.${mod}')
}
}
}
}

fn (mut p Parser) parse_update_query(query []string) {
q_urls := query.filter(it.starts_with('https://') || it.starts_with('git@'))
mut installed := map[string]InstalledModule{}
for entry in os.ls(settings.vmodules_path) or { return } {
path := os.join_path(settings.vmodules_path, entry)
if entry in excluded_dirs || !os.is_dir(path) {
continue
}
// Global modules `vmodules_dir/module`.
if os.exists(os.join_path(path, 'v.mod')) {
vcs := vcs_used_in_dir(path) or { continue }
args := vcs_info[vcs].args
// TODO: mercurial
url := os.execute_opt('${vcs.str()} ${args.path} ${path} config --get remote.origin.url') or {
vpm_error('failed to get url for `${entry}`.', details: err.msg())
continue
}.output.trim_space()
vpm_log(@FILE_LINE, @FN, 'url: ${url}')
mod := InstalledModule{
path: path
is_global: true
url: if url.starts_with('https://github.com/vlang')
|| url.starts_with('[email protected]:vlang') {
''
} else {
url
}
}
if url in q_urls {
installed[url] = mod
} else {
installed[entry] = mod
}
continue
}
// Modules under a publisher namespace `vmodules_dir/publisher/module`.
for mod in os.ls(path) or { continue } {
mod_path := os.join_path(path, mod)
if os.exists(os.join_path(mod_path, 'v.mod')) {
installed['${entry}.${mod}'] = InstalledModule{
path: mod_path
}
}
}
}
for m in query {
if m !in installed {
vpm_error('failed to update `${m}`. Not installed.')
p.errors++
continue
}
if !is_outdated(installed[m].path) {
verbose_println('Skipping `${m}`. Already up to date.')
continue
}
if installed[m].is_global && installed[m].url != '' {
p.parse_module(installed[m].url)
} else {
p.parse_module(m)
}
}
}

fn get_tmp_path(relative_path string) !string {
tmp_path := os.real_path(os.join_path(settings.tmp_path, relative_path))
if os.exists(tmp_path) {
Expand Down
77 changes: 36 additions & 41 deletions cmd/tools/vpm/update.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import os
import sync.pool
import v.help

struct UpdateSession {
idents []string
}

pub struct UpdateResult {
mut:
success bool
Expand All @@ -17,61 +13,60 @@ fn vpm_update(query []string) {
if settings.is_help {
help.print_and_exit('update')
}
idents := if query.len == 0 {
get_installed_modules()
mut p := Parser{}
if query.len == 0 {
p.parse_outdated()
} else {
query.clone()
p.parse_update_query(query)
}
// In case dependencies have changed, new modules may need to be installed.
mut to_update, mut to_install := []Module{}, []Module{}
for m in p.modules.values() {
if m.is_installed {
to_update << m
} else {
to_install << m
}
}
if to_update.len == 0 {
if p.errors > 0 {
exit(1)
} else {
println('All modules are up to date.')
exit(0)
}
}
vpm_log(@FILE_LINE, @FN, 'Modules to update: ${to_update}')
vpm_log(@FILE_LINE, @FN, 'Modules to install: ${to_install}')
mut pp := pool.new_pool_processor(callback: update_module)
ctx := UpdateSession{idents}
pp.set_shared_context(ctx)
pp.work_on_items(idents)
pp.work_on_items(to_update)
mut errors := 0
for res in pp.get_results[UpdateResult]() {
if !res.success {
errors++
continue
}
}
if errors > 0 {
if to_install.len != 0 {
install_modules(to_install)
}
if p.errors > 0 || errors > 0 {
exit(1)
}
}

fn update_module(mut pp pool.PoolProcessor, idx int, wid int) &UpdateResult {
ident := pp.get_item[string](idx)
// Usually, the module `ident`ifier. `get_name_from_url` is only relevant for `v update <module_url>`.
name := get_name_from_url(ident) or { ident }
install_path := get_path_of_existing_module(ident) or {
vpm_error('failed to find path for `${name}`.', verbose: true)
return &UpdateResult{}
}
vcs := vcs_used_in_dir(install_path) or {
vpm_error('failed to find version control system for `${name}`.', verbose: true)
return &UpdateResult{}
}
vcs.is_executable() or {
vpm_error(err.msg())
return &UpdateResult{}
}
m := pp.get_item[Module](idx)
vcs := m.vcs or { settings.vcs }
args := vcs_info[vcs].args
cmd := [vcs.str(), args.path, os.quoted_path(install_path), args.update].join(' ')
vpm_log(@FILE_LINE, @FN, 'cmd: ${cmd}')
println('Updating module `${name}` in `${fmt_mod_path(install_path)}`...')
cmd := [vcs.str(), args.path, os.quoted_path(m.install_path), args.update].join(' ')
vpm_log(@FILE_LINE, @FN, '> cmd: ${cmd}')
println('Updating module `${m.name}` in `${fmt_mod_path(m.install_path)}`...')
res := os.execute_opt(cmd) or {
vpm_error('failed to update module `${name}` in `${install_path}`.', details: err.msg())
vpm_error('failed to update module `${m.name}` in `${m.install_path}`.', details: err.msg())
return &UpdateResult{}
}
vpm_log(@FILE_LINE, @FN, 'cmd output: ${res.output.trim_space()}')
if res.output.contains('Already up to date.') {
println('Skipped module `${ident}`. Already up to date.')
} else {
println('Updated module `${ident}`.')
}
vpm_log(@FILE_LINE, @FN, '>> output: ${res.output.trim_space()}')
// Don't bail if the download count increment has failed.
increment_module_download_count(name) or { vpm_error(err.msg(), verbose: true) }
ctx := unsafe { &UpdateSession(pp.get_shared_context()) }
vpm_log(@FILE_LINE, @FN, 'ident: ${ident}; ctx: ${ctx}')
resolve_dependencies(get_manifest(install_path), ctx.idents)
increment_module_download_count(m.name) or { vpm_error(err.msg(), verbose: true) }
return &UpdateResult{true}
}

0 comments on commit 54f1f6b

Please sign in to comment.