Skip to content

Commit 3708061

Browse files
authored
tools.vpm: fix install --once for external modules, add already installed info, add test (#19702)
1 parent a2dabdd commit 3708061

File tree

2 files changed

+152
-73
lines changed

2 files changed

+152
-73
lines changed

cmd/tools/vpm/install_test.v

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import os
22
import v.vmod
33

4-
// Running tests appends a tsession path to VTMP, which is automatically cleaned up after the test.
5-
// The following will result in e.g. `$VTMP/tsession_7fe8e93bd740_1612958707536/test-vmodules/`.
6-
const test_path = os.join_path(os.vtmp_dir(), 'test-vmodules')
4+
const (
5+
v = os.quoted_path(@VEXE)
6+
// Running tests appends a tsession path to VTMP, which is automatically cleaned up after the test.
7+
// The following will result in e.g. `$VTMP/tsession_7fe8e93bd740_1612958707536/test-vmodules/`.
8+
test_path = os.join_path(os.vtmp_dir(), 'test-vmodules')
9+
)
710

811
fn testsuite_begin() {
912
os.setenv('VMODULES', test_path, true)
@@ -13,21 +16,8 @@ fn testsuite_end() {
1316
os.rmdir_all(test_path) or {}
1417
}
1518

16-
fn test_install_from_git_url() {
17-
res := os.execute(@VEXE + ' install https://github.com/vlang/markdown')
18-
assert res.exit_code == 0, res.output
19-
mod := vmod.from_file(os.join_path(test_path, 'markdown', 'v.mod')) or {
20-
assert false, err.str()
21-
return
22-
}
23-
assert mod.name == 'markdown'
24-
assert mod.dependencies == []string{}
25-
assert res.output.contains('Installing module "markdown" from "https://github.com/vlang/markdown')
26-
assert res.output.contains('Relocating module from "vlang/markdown" to "markdown"')
27-
}
28-
2919
fn test_install_from_vpm_ident() {
30-
res := os.execute(@VEXE + ' install nedpals.args')
20+
res := os.execute('${v} install nedpals.args')
3121
assert res.exit_code == 0, res.output
3222
mod := vmod.from_file(os.join_path(test_path, 'nedpals', 'args', 'v.mod')) or {
3323
assert false, err.str()
@@ -38,7 +28,7 @@ fn test_install_from_vpm_ident() {
3828
}
3929

4030
fn test_install_from_vpm_short_ident() {
41-
res := os.execute(@VEXE + ' install pcre')
31+
res := os.execute('${v} install pcre')
4232
assert res.exit_code == 0, res.output
4333
mod := vmod.from_file(os.join_path(test_path, 'pcre', 'v.mod')) or {
4434
assert false, err.str()
@@ -48,28 +38,78 @@ fn test_install_from_vpm_short_ident() {
4838
assert mod.description == 'A simple regex library for V.'
4939
}
5040

51-
fn test_install_already_existant() {
52-
// Skip on windows for now due permission errors with rmdir.
41+
fn test_install_from_git_url() {
42+
res := os.execute('${v} install https://github.com/vlang/markdown')
43+
assert res.exit_code == 0, res.output
44+
assert res.output.contains('Installing module "markdown" from "https://github.com/vlang/markdown')
45+
assert res.output.contains('Relocating module from "vlang/markdown" to "markdown"')
46+
mod := vmod.from_file(os.join_path(test_path, 'markdown', 'v.mod')) or {
47+
assert false, err.str()
48+
return
49+
}
50+
assert mod.name == 'markdown'
51+
assert mod.dependencies == []string{}
52+
}
53+
54+
fn test_install_already_existent() {
55+
// FIXME: Skip this for now on Windows, as `rmdir_all` results in permission
56+
// errors when vpm tries to remove existing modules.
5357
$if windows {
5458
return
5559
}
56-
mod_url := 'https://github.com/vlang/markdown'
57-
mut res := os.execute(@VEXE + ' install ${mod_url}')
58-
assert res.exit_code == 0, res.output
59-
res = os.execute(@VEXE + ' install ${mod_url}')
60+
mut res := os.execute('${v} install https://github.com/vlang/markdown')
6061
assert res.exit_code == 0, res.output
62+
assert res.output.contains('already exists')
6163
mod := vmod.from_file(os.join_path(test_path, 'markdown', 'v.mod')) or {
6264
assert false, err.str()
6365
return
6466
}
6567
assert mod.name == 'markdown'
6668
assert mod.dependencies == []string{}
67-
assert res.output.contains('already exists')
69+
}
70+
71+
fn test_install_once() {
72+
// Start with a clean test path.
73+
$if windows {
74+
// FIXME: Workaround for failing `rmdir` commands on Windows.
75+
os.system('rd /s /q ${test_path}')
76+
} $else {
77+
os.rmdir_all(test_path) or {}
78+
}
79+
os.mkdir_all(test_path) or {}
80+
81+
// Install markdown module.
82+
mut res := os.execute('${v} install markdown')
83+
assert res.exit_code == 0, res.output
84+
// Keep track of the last modified state of the v.mod file of the installed markdown module.
85+
md_last_modified := os.file_last_mod_unix(os.join_path(test_path, 'markdown', 'v.mod'))
86+
87+
install_cmd := '${@VEXE} install https://github.com/vlang/markdown https://github.com/vlang/pcre --once -v'
88+
// Try installing two modules, one of which is already installed.
89+
res = os.execute(install_cmd)
90+
assert res.exit_code == 0, res.output
91+
assert res.output.contains("Already installed modules: ['markdown']")
92+
mod := vmod.from_file(os.join_path(test_path, 'pcre', 'v.mod')) or {
93+
assert false, err.str()
94+
return
95+
}
96+
assert mod.name == 'pcre'
97+
assert mod.description == 'A simple regex library for V.'
98+
// Ensure the before installed markdown module wasn't modified.
99+
assert md_last_modified == os.file_last_mod_unix(os.join_path(test_path, 'markdown',
100+
'v.mod'))
101+
102+
// Try installing two modules that are both already installed.
103+
res = os.execute(install_cmd)
104+
assert res.exit_code == 0, res.output
105+
assert res.output.contains('All modules are already installed.')
106+
assert md_last_modified == os.file_last_mod_unix(os.join_path(test_path, 'markdown',
107+
'v.mod'))
68108
}
69109

70110
fn test_missing_repo_name_in_url() {
71111
incomplete_url := 'https://github.com/vlang'
72-
res := os.execute(@VEXE + ' install ${incomplete_url}')
112+
res := os.execute('${v} install ${incomplete_url}')
73113
assert res.exit_code == 1
74114
assert res.output.trim_space() == 'Errors while retrieving module name for: "${incomplete_url}"'
75115
}

cmd/tools/vpm/vpm.v

Lines changed: 86 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -65,50 +65,21 @@ fn main() {
6565
help.print_and_exit('vpm', exit_code: 5)
6666
}
6767
vpm_command := params[0]
68-
mut module_names := params[1..].clone()
68+
mut requested_modules := params[1..].clone()
6969
ensure_vmodules_dir_exist()
70-
// println('module names: ') println(module_names)
70+
// println('module names: ') println(requested_modules)
7171
match vpm_command {
7272
'help' {
7373
help.print_and_exit('vpm')
7474
}
7575
'search' {
76-
vpm_search(module_names)
76+
vpm_search(requested_modules)
7777
}
7878
'install' {
79-
if module_names.len == 0 && os.exists('./v.mod') {
80-
println('Detected v.mod file inside the project directory. Using it...')
81-
manifest := vmod.from_file('./v.mod') or { panic(err) }
82-
module_names = manifest.dependencies.clone()
83-
}
84-
85-
if '--once' in options {
86-
module_names = vpm_once_filter(module_names)
87-
88-
if module_names.len == 0 {
89-
return
90-
}
91-
}
92-
93-
external_module_names := module_names.filter(it.starts_with('https://'))
94-
vpm_module_names := module_names.filter(it !in external_module_names)
95-
96-
if vpm_module_names.len > 0 {
97-
vpm_install(vpm_module_names, Source.vpm)
98-
}
99-
100-
if external_module_names.len > 0 {
101-
mut external_source := Source.git
102-
103-
if '--hg' in options {
104-
external_source = Source.hg
105-
}
106-
107-
vpm_install(external_module_names, external_source)
108-
}
79+
vpm_install_(requested_modules, options)
10980
}
11081
'update' {
111-
vpm_update(module_names)
82+
vpm_update(requested_modules)
11283
}
11384
'upgrade' {
11485
vpm_upgrade()
@@ -120,10 +91,10 @@ fn main() {
12091
vpm_list()
12192
}
12293
'remove' {
123-
vpm_remove(module_names)
94+
vpm_remove(requested_modules)
12495
}
12596
'show' {
126-
vpm_show(module_names)
97+
vpm_show(requested_modules)
12798
}
12899
else {
129100
eprintln('Error: you tried to run "v ${vpm_command}"')
@@ -136,6 +107,85 @@ fn main() {
136107
}
137108
}
138109

110+
fn vpm_install_(requested_modules []string, opts []string) {
111+
if settings.is_help {
112+
help.print_and_exit('vpm')
113+
}
114+
115+
modules := if requested_modules.len == 0 {
116+
// Run `v install` in a directory of another V module without passing modules as arguments
117+
// to install its dependencies.
118+
if os.exists('./v.mod') {
119+
println('Detected v.mod file inside the project directory. Using it...')
120+
manifest := vmod.from_file('./v.mod') or { panic(err) }
121+
if manifest.dependencies.len == 0 {
122+
println('Nothing to install.')
123+
exit(0)
124+
}
125+
manifest.dependencies.clone()
126+
} else {
127+
eprintln('Specify a module for installation.')
128+
help.print_and_exit('vpm')
129+
return
130+
}
131+
} else {
132+
requested_modules.clone()
133+
}
134+
135+
mut external_modules := modules.filter(it.starts_with('https://'))
136+
mut vpm_modules := modules.filter(it !in external_modules)
137+
installed_modules := get_installed_modules()
138+
139+
if installed_modules.len > 0 && '--once' in opts {
140+
mut already_installed := []string{}
141+
if external_modules.len > 0 {
142+
mut i_deleted := []int{}
143+
for i, raw_url in external_modules {
144+
url := urllib.parse(raw_url) or {
145+
eprintln('Errors while parsing module url "${raw_url}" : ${err}')
146+
continue
147+
}
148+
mod_name := url.path.all_after_last('/')
149+
if mod_name in installed_modules {
150+
already_installed << mod_name
151+
i_deleted << i
152+
}
153+
}
154+
for i in i_deleted.reverse() {
155+
external_modules.delete(i)
156+
}
157+
}
158+
if vpm_modules.len > 0 {
159+
mut i_deleted := []int{}
160+
for i, mod_name in vpm_modules {
161+
if mod_name in installed_modules {
162+
already_installed << mod_name
163+
i_deleted << i
164+
continue
165+
}
166+
}
167+
for i in i_deleted.reverse() {
168+
vpm_modules.delete(i)
169+
}
170+
}
171+
if already_installed.len > 0 {
172+
verbose_println('Already installed modules: ${already_installed}')
173+
if already_installed.len == modules.len {
174+
verbose_println('All modules are already installed.')
175+
exit(0)
176+
}
177+
}
178+
}
179+
180+
if vpm_modules.len > 0 {
181+
vpm_install(vpm_modules, Source.vpm)
182+
}
183+
if external_modules.len > 0 {
184+
source := if '--hg' in opts { Source.hg } else { Source.git }
185+
vpm_install(external_modules, source)
186+
}
187+
}
188+
139189
fn vpm_search(keywords []string) {
140190
search_keys := keywords.map(it.replace('_', '-'))
141191
if settings.is_help {
@@ -349,17 +399,6 @@ fn vpm_install_from_vcs(modules []string, vcs_key string) {
349399
}
350400
}
351401

352-
fn vpm_once_filter(module_names []string) []string {
353-
installed_modules := get_installed_modules()
354-
mut toinstall := []string{}
355-
for mn in module_names {
356-
if mn !in installed_modules {
357-
toinstall << mn
358-
}
359-
}
360-
return toinstall
361-
}
362-
363402
fn vpm_install(module_names []string, source Source) {
364403
if settings.is_help {
365404
help.print_and_exit('install')

0 commit comments

Comments
 (0)