Skip to content

Commit f31657d

Browse files
committed
Support multiple output directories #18
Allow multiple -o flags by introducing Preferences.output_dirs (replacing single output_dir). CLI parsing now appends each output path into output_dirs and validates at least one is provided. PEX backend writes generated .pex files to the first output dir and copies them to any additional dirs. Original backend now warns when multiple output dirs are used (only the first dir is used). Added a new warning message constant and adjusted error/message string formatting. Updated tests to use output_dirs and added test_multiple_output_dirs. CHANGELOG updated to mention the new feature.
1 parent 2cfa970 commit f31657d

9 files changed

Lines changed: 109 additions & 31 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
## Next Release
22

3-
...
3+
### New Features
44

5-
## V 0.0.4
5+
- Added support for multiple output directories - you can now specify multiple `-o` flags to copy compiled .pex files to multiple locations. #18
6+
7+
...
68

79
### New Features
810

modules/builder/builder.v

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,12 @@ pub fn (mut b Builder) run() bool {
6868
b.print("used header dirs ${b.pref.header_dirs}")
6969

7070
b.files, b.files_names = find_all_src_files(b.pref.paths)
71-
71+
72+
// Warn if using multiple output directories with -original flag
73+
if b.pref.output_dirs.len > 1 && b.pref.backend == .original {
74+
b.print("Warning: Using multiple output directories with -original flag is not fully supported. Files will only be compiled to the first directory.")
75+
}
76+
7277
match b.pref.backend {
7378
.check,
7479
.pex {

modules/builder/original.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn (mut b Builder) compile_original() {
1515
header_dirs = header_dirs[..header_dirs.len-1]
1616

1717
for file in b.files {
18-
cmd := '"${compiler_exe_path}" "${file}" -quiet -i="${header_dirs}" -o="${b.pref.output_dir}" -f="${compiler_flags_path}"'
18+
cmd := '"${compiler_exe_path}" "${file}" -quiet -i="${header_dirs}" -o="${b.pref.output_dirs[0]}" -f="${compiler_flags_path}"'
1919

2020
b.print("executing: `${cmd}`")
2121

modules/builder/pex.v

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ fn (mut b Builder) compile_pex() {
9999
fn (mut b Builder) gen_to_pex_file(mut parsed_file ast.File, mut buff_bytes pex.Buffer) {
100100
if is_outdated(parsed_file, b.pref) {
101101
output_file_name := parsed_file.file_name + ".pex"
102-
output_file_path := os.join_path(b.pref.output_dir, output_file_name)
102+
// Use first output directory for writing
103+
output_file_path := os.join_path(b.pref.output_dirs[0], output_file_name)
103104

104105
mut pex_file := b.generator.gen(mut parsed_file)
105106

@@ -114,6 +115,16 @@ fn (mut b Builder) gen_to_pex_file(mut parsed_file ast.File, mut buff_bytes pex.
114115
util.fatal_error("failed to write file: ${err}")
115116
}
116117
file.close()
118+
119+
// Copy to additional output directories
120+
if b.pref.output_dirs.len > 1 {
121+
for j in 1 .. b.pref.output_dirs.len {
122+
copy_path := os.join_path(b.pref.output_dirs[j], output_file_name)
123+
os.cp(output_file_path, copy_path) or {
124+
util.fatal_error("failed to copy file to ${copy_path}: ${err}")
125+
}
126+
}
127+
}
117128
}
118129
}
119130

modules/papyrus/errors/errors.v

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,20 @@ pub:
3535
reporter Reporter
3636
}
3737

38-
pub const msg_invalid_output_path = "Error: Invalid path specified for the -o flag. Please provide a valid directory path where compiled files should be saved.";
39-
pub const msg_invalid_input_path = "Error: Invalid path specified for the -i flag. Please provide a valid directory or file path containing the .psc scripts to compile.";
40-
pub const msg_invalid_headers_path = "Error: Invalid path specified for the -h flag. Please provide a valid directory path containing header/import .psc files.";
38+
pub const msg_invalid_output_path = "Error: Invalid path specified for the -o flag. Please provide a valid directory path where compiled files should be saved."
39+
pub const msg_invalid_input_path = "Error: Invalid path specified for the -i flag. Please provide a valid directory or file path containing the .psc scripts to compile."
40+
pub const msg_invalid_headers_path = "Error: Invalid path specified for the -h flag. Please provide a valid directory path containing header/import .psc files."
4141
pub const msg_duplicate_input_flag = "Error: Duplicate -i flag detected with the same path. Please ensure each -i flag points to a unique directory or file."
42-
pub const msg_duplicate_output_flag = "Error: The -o flag has already been specified. Please provide only one output directory.";
43-
pub const msg_missing_input = "Error: Missing mandatory -i flag. Please specify the directory or file containing .psc scripts to compile.";
44-
pub const msg_missing_output = "Error: Missing mandatory -o flag. Please specify the directory where compiled .pex files should be saved.";
42+
pub const msg_duplicate_output_flag = "Error: The -o flag has already been specified. Please provide only one output directory."
43+
pub const msg_missing_input = "Error: Missing mandatory -i flag. Please specify the directory or file containing .psc scripts to compile."
44+
pub const msg_missing_output = "Error: Missing mandatory -o flag. Please specify the directory where compiled .pex files should be saved."
45+
pub const msg_warning_multiple_output_with_original = "Warning: Using multiple output directories with -original flag is not fully supported. Files will only be compiled to the first directory."
4546
//TODO
46-
pub const msg_wrong_number_of_arguments = "Error: Incorrect number of arguments for the command. Please refer to the documentation for the correct usage.";
47+
pub const msg_wrong_number_of_arguments = "Error: Incorrect number of arguments for the command. Please refer to the documentation for the correct usage."
4748
//TODO
48-
pub const msg_missing_or_incorrect_argument = "Error: Missing or incorrect argument. Please check the syntax of your command.";
49-
pub const msg_missing_or_incorrect_command = "Error: Invalid command. Please use one of the following commands: compile, read, disassembly, create-dump, help.";
50-
pub const msg_invalid_path_disassembly = "Error: Invalid path specified for the disassembly command. Please provide a valid file path for the .pex file.";
51-
pub const msg_invalid_path_read = "Error: Invalid path specified for the read command. Please provide a valid file path for the .pex file.";
52-
pub const msg_invalid_path_create_dump = "Error: Invalid path specified for create-dump. Please provide a valid directory path containing .pex files.";
49+
pub const msg_missing_or_incorrect_argument = "Error: Missing or incorrect argument. Please check the syntax of your command."
50+
pub const msg_missing_or_incorrect_command = "Error: Invalid command. Please use one of the following commands: compile, read, disassembly, create-dump, help."
51+
pub const msg_invalid_path_disassembly = "Error: Invalid path specified for the disassembly command. Please provide a valid file path for the .pex file."
52+
pub const msg_invalid_path_read = "Error: Invalid path specified for the read command. Please provide a valid file path for the .pex file."
53+
pub const msg_invalid_path_create_dump = "Error: Invalid path specified for create-dump. Please provide a valid directory path containing .pex files."
5354

modules/pref/pref.v

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub fn (m RunMode) str() string {
4747
pub struct Preferences {
4848
pub mut:
4949
paths []string // folders with files to compile
50-
output_dir string // folder for output files
50+
output_dirs []string // folders for output files
5151
mode RunMode = .compile
5252
backend Backend = .pex
5353
no_cache bool
@@ -71,7 +71,9 @@ pub fn (p Preferences) cmd_str() string {
7171
b.write_string("-i \"${path}\" ")
7272
}
7373

74-
b.write_string("-o \"${p.output_dir}\" ")
74+
for dir in p.output_dirs {
75+
b.write_string("-o \"${dir}\" ")
76+
}
7577

7678
for dir in p.header_dirs {
7779
b.write_string("-h \"${dir}\" ")
@@ -124,17 +126,13 @@ fn (mut p Preferences) parse_compile_args(args []string) {
124126
"-output" {
125127
i++
126128

127-
if p.output_dir != "" {
128-
error(errors.msg_duplicate_output_flag) // path
129-
}
130-
131129
path := os.real_path(args[i])
132130

133131
if !os.is_dir(path) {
134132
error(errors.msg_invalid_output_path) // path
135133
}
136134

137-
p.output_dir = path
135+
p.output_dirs << path
138136
i++
139137
}
140138
"-h",
@@ -192,7 +190,7 @@ fn (mut p Preferences) parse_compile_args(args []string) {
192190
error(errors.msg_missing_input)
193191
}
194192

195-
if p.output_dir == "" {
193+
if p.output_dirs.len == 0 {
196194
error(errors.msg_missing_output)
197195
}
198196
}

modules/tests/projects_test.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import builder
77
fn get_prefs(input_dir string, header_dirs []string, output_dir string) pref.Preferences {
88
return pref.Preferences {
99
paths: [ input_dir ]
10-
output_dir: output_dir
10+
output_dirs: [ output_dir ]
1111
mode: .compile
1212
backend: .check
1313
no_cache: true

modules/tests/selective_headers_loading_test.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ fn test_selective_headers_loading() {
3535

3636
prefs := pref.Preferences {
3737
paths: [ src_file ]
38-
output_dir: output_dir
38+
output_dirs: [ output_dir ]
3939
mode: .compile
4040
backend: .check
4141
no_cache: true

modules/tests/vm_tests_test.v

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@ fn test_builder() {
2626

2727
prefs := pref.Preferences {
2828
paths: [ input_dir ]
29-
output_dir: output_dir
29+
output_dirs: [ output_dir ]
3030
mode: .compile
3131
backend: .pex
3232
no_cache: true
3333
header_dirs: []
3434
output_mode: .silent
3535
}
3636

37-
out_file1 := os.join_path(prefs.output_dir, "AAATestObject.pex")
38-
out_file2 := os.join_path(prefs.output_dir, "LatentTest.pex")
39-
out_file3 := os.join_path(prefs.output_dir, "OpcodesTest.pex")
37+
out_file1 := os.join_path(prefs.output_dirs[0], "AAATestObject.pex")
38+
out_file2 := os.join_path(prefs.output_dirs[0], "LatentTest.pex")
39+
out_file3 := os.join_path(prefs.output_dirs[0], "OpcodesTest.pex")
4040

4141
if os.is_file(out_file1) {
4242
os.rm(out_file1) or { assert false, "failed to delete file" }
@@ -100,4 +100,65 @@ fn test_builder() {
100100
for func in pex3.objects[0].states[0].functions {
101101
assert pex3.get_string(func.name) in pex3_string_table
102102
}
103+
}
104+
105+
fn test_multiple_output_dirs() {
106+
input_dir := os.real_path('test-files/vm-tests')
107+
output_dir1 := os.real_path('test-files/compiled/multi1')
108+
output_dir2 := os.real_path('test-files/compiled/multi2')
109+
110+
file1 := os.join_path(input_dir, "AAATestObject.psc")
111+
112+
if !os.is_file(file1) {
113+
assert false, "invalid input file ${file1}"
114+
}
115+
116+
// Create output directories
117+
if !os.is_dir(output_dir1) {
118+
os.mkdir(output_dir1, os.MkdirParams{}) or { assert false, "failed to create output_dir1" }
119+
}
120+
if !os.is_dir(output_dir2) {
121+
os.mkdir(output_dir2, os.MkdirParams{}) or { assert false, "failed to create output_dir2" }
122+
}
123+
124+
prefs := pref.Preferences {
125+
paths: [ input_dir ]
126+
output_dirs: [ output_dir1, output_dir2 ]
127+
mode: .compile
128+
backend: .pex
129+
no_cache: true
130+
header_dirs: []
131+
output_mode: .silent
132+
}
133+
134+
out_file1 := os.join_path(prefs.output_dirs[0], "AAATestObject.pex")
135+
out_file2 := os.join_path(prefs.output_dirs[1], "AAATestObject.pex")
136+
137+
// Clean up previous runs
138+
if os.is_file(out_file1) {
139+
os.rm(out_file1) or { assert false, "failed to delete file" }
140+
}
141+
if os.is_file(out_file2) {
142+
os.rm(out_file2) or { assert false, "failed to delete file" }
143+
}
144+
145+
builder.compile(&prefs)
146+
147+
// Verify file exists in first output directory
148+
assert os.is_file(out_file1), "pex file should exist in first output directory"
149+
150+
// Verify file was copied to second output directory
151+
assert os.is_file(out_file2), "pex file should exist in second output directory (copied)"
152+
153+
// Verify both files are identical
154+
pex1_content := os.read_bytes(out_file1) or { panic("failed to read pex1") }
155+
pex2_content := os.read_bytes(out_file2) or { panic("failed to read pex2") }
156+
assert pex1_content.len == pex2_content.len, "both pex files should have same size"
157+
assert pex1_content == pex2_content, "both pex files should be identical"
158+
159+
// Clean up
160+
os.rm(out_file1) or {}
161+
os.rm(out_file2) or {}
162+
os.rmdir(output_dir1) or {}
163+
os.rmdir(output_dir2) or {}
103164
}

0 commit comments

Comments
 (0)