| 1 | module main |
| 2 | |
| 3 | import os |
| 4 | import v.help |
| 5 | import v.vmod |
| 6 | |
| 7 | struct LinkedProject { |
| 8 | name string |
| 9 | project_dir string |
| 10 | link_path string |
| 11 | } |
| 12 | |
| 13 | fn 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 | |
| 54 | fn 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 | |
| 80 | fn 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 | |
| 90 | fn 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 | |
| 114 | fn 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 | |
| 124 | fn 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 | |