v2 / cmd / tools / vpm / link.v
138 lines · 128 sloc · 3.8 KB · f6e41da47be431a5e9b2d83c13eb3b594c13c3fa
Raw
1module main
2
3import os
4import v.help
5import v.vmod
6
7struct LinkedProject {
8 name string
9 project_dir string
10 link_path string
11}
12
13fn vpm_link(query []string) {
14 if settings.is_help {
15 help.print_and_exit('link')
16 }
17 ensure_no_query_for_project_command('link', query)
18 ensure_vmodules_dir_exist()
19
20 project := get_project_for_linking() or {
21 vpm_error(err.msg())
22 exit(1)
23 }
24 if project.project_dir == os.real_path(project.link_path) {
25 println('Module `${project.name}` is already available in `${fmt_mod_path(project.link_path)}`.')
26 return
27 }
28 if os.exists(project.link_path) || os.is_link(project.link_path) {
29 if os.is_link(project.link_path) {
30 if os.real_path(project.link_path) == project.project_dir {
31 println('Module `${project.name}` is already linked in `${fmt_mod_path(project.link_path)}`.')
32 return
33 }
34 vpm_error('`${project.name}` is already linked at `${fmt_mod_path(project.link_path)}`.',
35 details: 'Run `v unlink` first to replace it.'
36 )
37 exit(1)
38 }
39 vpm_error('`${project.name}` already exists at `${fmt_mod_path(project.link_path)}`.')
40 exit(1)
41 }
42 parent_dir := os.dir(project.link_path)
43 os.mkdir_all(parent_dir) or {
44 vpm_error('failed to create `${fmt_mod_path(parent_dir)}`.', details: err.msg())
45 exit(1)
46 }
47 os.symlink(project.project_dir, project.link_path) or {
48 vpm_error('failed to link `${project.name}`.', details: err.msg())
49 exit(1)
50 }
51 println('Linked `${project.name}` to `${fmt_mod_path(project.link_path)}`.')
52}
53
54fn vpm_unlink(query []string) {
55 if settings.is_help {
56 help.print_and_exit('unlink')
57 }
58 ensure_no_query_for_project_command('unlink', query)
59
60 project := get_project_for_linking() or {
61 vpm_error(err.msg())
62 exit(1)
63 }
64 if !os.exists(project.link_path) && !os.is_link(project.link_path) {
65 println('Module `${project.name}` is not linked in `${fmt_mod_path(project.link_path)}`.')
66 return
67 }
68 if !os.is_link(project.link_path) {
69 vpm_error('`${project.name}` at `${fmt_mod_path(project.link_path)}` is not a symlink.')
70 exit(1)
71 }
72 remove_symlink(project.link_path) or {
73 vpm_error('failed to unlink `${project.name}`.', details: err.msg())
74 exit(1)
75 }
76 cleanup_empty_link_parent_dirs(project.link_path)
77 println('Unlinked `${project.name}` from `${fmt_mod_path(project.link_path)}`.')
78}
79
80fn ensure_no_query_for_project_command(command string, query []string) {
81 if query.len == 0 {
82 return
83 }
84 vpm_error('`${command}` does not accept package names.',
85 details: 'Run `v ${command}` from inside the project directory.'
86 )
87 exit(2)
88}
89
90fn get_project_for_linking() !LinkedProject {
91 wrkdir := os.getwd()
92 mut mcache := vmod.get_cache()
93 vmod_location := mcache.get_by_folder(wrkdir)
94 if vmod_location.vmod_file == '' {
95 return error('no `v.mod` file found in `${wrkdir}` or its parent directories.')
96 }
97 manifest := vmod.from_file(vmod_location.vmod_file)!
98 if manifest.name.trim_space() == '' {
99 return error('`${vmod_location.vmod_file}` is missing the `name` field.')
100 }
101 mod_path := normalize_mod_path(manifest.name.replace('.', os.path_separator))
102 vmodules_path := if os.is_dir(settings.vmodules_path) {
103 os.real_path(settings.vmodules_path)
104 } else {
105 settings.vmodules_path
106 }
107 return LinkedProject{
108 name: manifest.name
109 project_dir: os.real_path(vmod_location.vmod_folder)
110 link_path: os.join_path(vmodules_path, mod_path)
111 }
112}
113
114fn remove_symlink(path string) ! {
115 os.rm(path) or {
116 $if windows {
117 os.rmdir(path)!
118 } $else {
119 return err
120 }
121 }
122}
123
124fn cleanup_empty_link_parent_dirs(link_path string) {
125 vmodules_path := if os.is_dir(settings.vmodules_path) {
126 os.real_path(settings.vmodules_path)
127 } else {
128 settings.vmodules_path
129 }
130 mut parent := os.dir(link_path)
131 for parent != vmodules_path && parent != os.dir(parent) {
132 if !os.is_dir(parent) || !os.is_dir_empty(parent) {
133 break
134 }
135 os.rmdir(parent) or { break }
136 parent = os.dir(parent)
137 }
138}
139