| 1 | import os |
| 2 | import log |
| 3 | |
| 4 | const should_clean = os.args.contains('-c') |
| 5 | |
| 6 | fn main() { |
| 7 | log.use_stdout() |
| 8 | unbuffer_stdout() |
| 9 | mut files := []string{} |
| 10 | args := os.args#[2..].filter(it != '-c') |
| 11 | for a in args { |
| 12 | if os.is_file(a) { |
| 13 | files << a |
| 14 | continue |
| 15 | } |
| 16 | if os.is_dir(a) { |
| 17 | files << os.walk_ext(a, '.v') |
| 18 | continue |
| 19 | } |
| 20 | } |
| 21 | files.sort() |
| 22 | if files.len == 0 { |
| 23 | println('0 .v files found.\n') |
| 24 | println('Usage:') |
| 25 | println(' v should-compile-all [-c] examples/ some/deep/file.v another/') |
| 26 | println('... will try to compile all .v files found in the given folders and files, one by one.') |
| 27 | println('If every single one of them compiles, the command will exit with an error code of 0.') |
| 28 | println('If *any* of them *fail* to compile, the command will exit with an error code of 1.') |
| 29 | println('') |
| 30 | println(' -c will remove all the compiled executables at the end.') |
| 31 | println('') |
| 32 | println('Note: this command is intended to be used in CI pipelines for v modules, like this:') |
| 33 | println(' cd module/ ; v should-compile-all examples/ \n') |
| 34 | exit(1) |
| 35 | } |
| 36 | mut executables := []string{} |
| 37 | mut failed_commands := []string{} |
| 38 | mut project_folders := map[string]bool{} |
| 39 | mut skipped_files := []string{} |
| 40 | for idx, example in files { |
| 41 | folder_of_example := os.dir(example) |
| 42 | if os.is_file(os.join_path_single(folder_of_example, '.skip_should_compile_all')) { |
| 43 | log.info('>>> skipping file: ${example}, because a `.skip_should_compile_all` file is present next to it.') |
| 44 | skipped_files << example |
| 45 | continue |
| 46 | } |
| 47 | // project folders usually contain many .v files, that are *all* part of the same program. |
| 48 | // NOTE: => projects should be compiled with `v project/`. |
| 49 | // To do that, just record the presence of such a folder for now, and try to compile it separately later. |
| 50 | if project_folders[folder_of_example] { |
| 51 | skipped_files << example |
| 52 | continue |
| 53 | } |
| 54 | if os.is_file(os.join_path_single(folder_of_example, 'v.mod')) { |
| 55 | log.info('>>> delaying compilation of entire project folder ${folder_of_example} ...') |
| 56 | project_folders[folder_of_example] = true |
| 57 | skipped_files << example |
| 58 | continue |
| 59 | } |
| 60 | // |
| 61 | mut backend_options := '-b c' |
| 62 | if example.ends_with('.wasm.v') { |
| 63 | backend_options = '-b wasm -os browser' |
| 64 | } |
| 65 | if example.ends_with('.js.v') { |
| 66 | backend_options = '-b js' |
| 67 | } |
| 68 | lines := os.read_lines(example)!#[..50].filter(it.starts_with('module')) |
| 69 | if lines.len > 0 && lines[0] !in ['module main', 'module no_main'] { |
| 70 | log.info('>>> skipping non main module file: ${example}') |
| 71 | skipped_files << example |
| 72 | continue |
| 73 | } |
| 74 | cmd := '${os.quoted_path(@VEXE)} ${backend_options} ${os.quoted_path(example)}' |
| 75 | log.info('> compiling program ${idx + 1:4}/${files.len:-4}: ${cmd}') |
| 76 | if 0 != os.system(cmd) { |
| 77 | failed_commands << cmd |
| 78 | } else { |
| 79 | executables << executable_name(example) |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | mut glsl_folders := map[string]bool{} |
| 84 | mut pfi := 0 |
| 85 | for pf, _ in project_folders { |
| 86 | glsl_files := os.walk_ext(pf, '.glsl') |
| 87 | if glsl_files.len > 0 { |
| 88 | if pf !in glsl_folders { |
| 89 | log.debug('>>> found .glsl files in ${pf} ... running `v shader ${pf}` ...') |
| 90 | os.system('${os.quoted_path(@VEXE)} shader ${os.quoted_path(pf)}') |
| 91 | glsl_folders[pf] = true |
| 92 | } |
| 93 | } |
| 94 | exe_path := os.join_path(pf, os.file_name(pf) + exe_extension) |
| 95 | cmd := '${os.quoted_path(@VEXE)} -o ${exe_path} ${pf}' |
| 96 | log.info('> compiling project ${pfi + 1:4}/${project_folders.len:-4}: ${cmd}') |
| 97 | if 0 != os.system(cmd) { |
| 98 | failed_commands << cmd |
| 99 | } else { |
| 100 | executables << exe_path |
| 101 | } |
| 102 | pfi++ |
| 103 | } |
| 104 | |
| 105 | if should_clean { |
| 106 | log.info('Removing ${executables.len} successfully build executables...') |
| 107 | for f in executables { |
| 108 | os.rm(f) or { log.error('>> could not remove ${f}, err: ${err}') } |
| 109 | } |
| 110 | } |
| 111 | if failed_commands.len > 0 { |
| 112 | for idx, fcmd in failed_commands { |
| 113 | log.error('>>> FAILED command ${idx + 1:4}/${failed_commands.len:-4}: ${fcmd}') |
| 114 | } |
| 115 | log.info('Summary: ${failed_commands.len:4}/${files.len:-4} file(s) failed to compile.') |
| 116 | exit(1) |
| 117 | } |
| 118 | log.info('Summary: all ${files.len} program file(s), and ${project_folders.len} project(s) compiled successfully. Skipped files: ${skipped_files.len} .') |
| 119 | } |
| 120 | |
| 121 | const exe_extension = if os.user_os() == 'windows' { |
| 122 | '.exe' |
| 123 | } else { |
| 124 | '' |
| 125 | } |
| 126 | |
| 127 | fn executable_name(source string) string { |
| 128 | basepath := source.replace(os.file_ext(source), '') |
| 129 | return basepath + exe_extension |
| 130 | } |
| 131 | |