module main import os import v.vmod import v.help fn vpm_install(query []string) { if settings.is_help { help.print_and_exit('vpm') } mut vpm_modules, mut external_modules := parse_query(if query.len == 0 { if os.exists('./v.mod') { // Case: `v install` was run in a directory of another V-module to install its dependencies // - without additional module arguments. println('Detected v.mod file inside the project directory. Using it...') manifest := vmod.from_file('./v.mod') or { panic(err) } if manifest.dependencies.len == 0 { println('Nothing to install.') exit(0) } manifest.dependencies.clone() } else { vpm_error('specify at least one module for installation.', details: 'example: `v install publisher.package` or `v install https://github.com/owner/repository`' ) exit(2) } } else { query }) installed_modules := get_installed_modules() vpm_log(@FILE_LINE, @FN, 'VPM modules: ${vpm_modules}') vpm_log(@FILE_LINE, @FN, 'External modules: ${external_modules}') vpm_log(@FILE_LINE, @FN, 'Installed modules: ${installed_modules}') if installed_modules.len > 0 && settings.is_once { mut already_installed := []string{} if external_modules.len > 0 { mut i_deleted := []int{} for i, m in external_modules { if m.name in installed_modules { already_installed << m.name i_deleted << i } } for i in i_deleted.reverse() { external_modules.delete(i) } } if vpm_modules.len > 0 { mut i_deleted := []int{} for i, m in vpm_modules { if m.name in installed_modules { already_installed << m.name i_deleted << i } } for i in i_deleted.reverse() { vpm_modules.delete(i) } } if already_installed.len > 0 { verbose_println('Already installed modules: ${already_installed}') if already_installed.len == query.len { println('All modules are already installed.') exit(0) } } } if vpm_modules.len > 0 { vpm_install_from_vpm(vpm_modules) } if external_modules.len > 0 { vpm_install_from_vcs(external_modules) } } fn vpm_install_from_vpm(modules []Module) { vpm_log(@FILE_LINE, @FN, 'modules: ${modules}') idents := modules.map(it.name) mut errors := 0 for m in modules { vpm_log(@FILE_LINE, @FN, 'module: ${m}') last_errors := errors vcs := if m.vcs != '' { supported_vcs[m.vcs] or { errors++ vpm_error('skipping `${m.name}`, since it uses an unsupported version control system `${m.vcs}`.') continue } } else { supported_vcs['git'] } vcs.is_executable() or { vpm_error(err.msg()) errors++ continue } if os.exists(m.install_path) { vpm_update([m.name]) continue } m.install(vcs) or { errors++ vpm_error(err.msg()) continue } increment_module_download_count(m.name) or { errors++ vpm_error('failed to increment the download count for `${m.name}`', details: err.msg()) } if last_errors == errors { println('Installed `${m.name}`.') } resolve_dependencies(get_manifest(m.install_path), idents) } if errors > 0 { exit(1) } } fn vpm_install_from_vcs(modules []Module) { mut errors := 0 vcs := supported_vcs[settings.vcs] urls := modules.map(it.url) for m in modules { vpm_log(@FILE_LINE, @FN, 'module: ${m}') last_errors := errors if os.exists(m.install_path) { vpm_update([m.name]) continue } vcs.is_executable() or { vpm_error(err.msg()) errors++ continue } m.install(vcs) or { errors++ vpm_error(err.msg()) continue } // Note: increment error count when v.mod becomes mandatory for external modules. manifest := get_manifest(m.install_path) or { continue } final_path := os.real_path(os.join_path(settings.vmodules_path, manifest.name.replace('-', '_').to_lower())) if m.install_path != final_path { verbose_println('Relocating `${m.name} (${m.install_path})` to `${manifest.name} (${final_path})`...') if os.exists(final_path) { println('Target directory for `${m.name} (${final_path})` already exists.') input := os.input('Replace it with the module directory? [Y/n]: ') match input.to_lower() { '', 'y' { m.remove() or { vpm_error('failed to remove `${final_path}`.', details: err.msg()) errors++ continue } } else { verbose_println('Skipping `${m.name}`.') continue } } } // When the module should be relocated into a subdirectory we need to make sure // it exists to not run into permission errors. if m.install_path.count(os.path_separator) < final_path.count(os.path_separator) && !os.exists(final_path) { os.mkdir_all(final_path) or { vpm_error('failed to create directory for `${manifest.name}`.', details: err.msg() ) errors++ continue } } os.mv(m.install_path, final_path) or { errors++ vpm_error('failed to relocate module `${m.name}`.', details: err.msg()) os.rmdir_all(m.install_path) or { vpm_error('failed to remove `${m.install_path}`.', details: err.msg()) errors++ continue } continue } verbose_println('Relocated `${m.name}` to `${manifest.name}`.') } if last_errors == errors { println('Installed `${m.name}`.') } resolve_dependencies(manifest, urls) } if errors > 0 { exit(1) } } fn (m Module) install(vcs &VCS) ! { cmd := '${vcs.cmd} ${vcs.args.install} "${m.url}" "${m.install_path}"' vpm_log(@FILE_LINE, @FN, 'command: ${cmd}') println('Installing module `${m.name}` from `${m.url}` to `${m.install_path}` ...') os.execute_opt(cmd) or { vpm_log(@FILE_LINE, @FN, 'cmd output: ${err}') return error('failed to install module `${m.name}` to `${m.install_path}`.') } } fn (m Module) remove() ! { verbose_println('Removing `${m.name}` from `${m.install_path}`...') $if windows { os.execute_opt('rd /s /q ${m.install_path}')! } $else { os.rmdir_all(m.install_path)! } verbose_println('Removed `${m.name}`.') }