rscons 1.4.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/lib/rscons.rb +75 -5
  2. data/lib/rscons/build_target.rb +36 -0
  3. data/lib/rscons/builder.rb +41 -3
  4. data/lib/rscons/builders/cfile.rb +15 -0
  5. data/lib/rscons/builders/disassemble.rb +15 -0
  6. data/lib/rscons/builders/library.rb +17 -2
  7. data/lib/rscons/builders/object.rb +37 -5
  8. data/lib/rscons/builders/preprocess.rb +15 -0
  9. data/lib/rscons/builders/program.rb +42 -2
  10. data/lib/rscons/cache.rb +26 -6
  11. data/lib/rscons/environment.rb +259 -19
  12. data/lib/rscons/varset.rb +33 -10
  13. data/lib/rscons/version.rb +1 -1
  14. data/rscons.gemspec +8 -10
  15. metadata +38 -103
  16. checksums.yaml +0 -15
  17. data/.gitignore +0 -18
  18. data/.rspec +0 -2
  19. data/Gemfile +0 -4
  20. data/README.md +0 -384
  21. data/Rakefile.rb +0 -26
  22. data/build_tests/build_dir/src/one/one.c +0 -6
  23. data/build_tests/build_dir/src/two/two.c +0 -7
  24. data/build_tests/build_dir/src/two/two.h +0 -6
  25. data/build_tests/clone_env/src/program.c +0 -6
  26. data/build_tests/custom_builder/program.c +0 -7
  27. data/build_tests/d/main.d +0 -6
  28. data/build_tests/header/header.c +0 -7
  29. data/build_tests/header/header.h +0 -6
  30. data/build_tests/library/one.c +0 -8
  31. data/build_tests/library/three.c +0 -0
  32. data/build_tests/library/two.c +0 -0
  33. data/build_tests/simple/simple.c +0 -6
  34. data/build_tests/simple_cc/simple.cc +0 -8
  35. data/build_tests/two_sources/one.c +0 -8
  36. data/build_tests/two_sources/two.c +0 -3
  37. data/spec/build_tests_spec.rb +0 -527
  38. data/spec/rscons/builders/cfile_spec.rb +0 -28
  39. data/spec/rscons/builders/disassemble_spec.rb +0 -17
  40. data/spec/rscons/builders/library_spec.rb +0 -18
  41. data/spec/rscons/builders/object_spec.rb +0 -23
  42. data/spec/rscons/builders/preprocess_spec.rb +0 -18
  43. data/spec/rscons/builders/program_spec.rb +0 -18
  44. data/spec/rscons/cache_spec.rb +0 -271
  45. data/spec/rscons/environment_spec.rb +0 -361
  46. data/spec/rscons/varset_spec.rb +0 -163
  47. data/spec/rscons_spec.rb +0 -26
  48. data/spec/spec_helper.rb +0 -7
@@ -1,28 +0,0 @@
1
- module Rscons
2
- module Builders
3
- describe CFile do
4
- let(:env) {Environment.new}
5
- subject {CFile.new}
6
-
7
- it "invokes bison to create a .c file from a .y file" do
8
- subject.should_receive(:standard_build).with("YACC parser.c", "parser.c", ["bison", "-d", "-o", "parser.c", "parser.y"], ["parser.y"], env, :cache)
9
- subject.run("parser.c", ["parser.y"], :cache, env, {})
10
- end
11
-
12
- it "invokes a custom lexer to create a .cc file from a .ll file" do
13
- env["LEX"] = "custom_lex"
14
- subject.should_receive(:standard_build).with("LEX lexer.cc", "lexer.cc", ["custom_lex", "-o", "lexer.cc", "parser.ll"], ["parser.ll"], env, :cache)
15
- subject.run("lexer.cc", ["parser.ll"], :cache, env, {})
16
- end
17
-
18
- it "supports overriding construction variables" do
19
- subject.should_receive(:standard_build).with("LEX lexer.c", "lexer.c", ["hi", "parser.l"], ["parser.l"], env, :cache)
20
- subject.run("lexer.c", ["parser.l"], :cache, env, "LEX_CMD" => ["hi", "${_SOURCES}"])
21
- end
22
-
23
- it "raises an error when an unknown source file is specified" do
24
- expect {subject.run("file.c", ["foo.bar"], :cache, env, {})}.to raise_error /Unknown source file .foo.bar. for CFile builder/
25
- end
26
- end
27
- end
28
- end
@@ -1,17 +0,0 @@
1
- module Rscons
2
- module Builders
3
- describe Disassemble do
4
- let(:env) {Environment.new}
5
- subject {Disassemble.new}
6
-
7
- it "supports overriding DISASM_CMD construction variable" do
8
- cache = "cache"
9
- cache.stub(:up_to_date?) { false }
10
- cache.stub(:mkdir_p) { }
11
- cache.stub(:register_build) { }
12
- env.should_receive(:execute).with("Disassemble a_file.txt", ["my_disasm", "a_file.exe"], anything).and_return(true)
13
- subject.run("a_file.txt", ["a_file.exe"], cache, env, "DISASM_CMD" => ["my_disasm", "${_SOURCES}"])
14
- end
15
- end
16
- end
17
- end
@@ -1,18 +0,0 @@
1
- module Rscons
2
- module Builders
3
- describe Library do
4
- let(:env) {Environment.new}
5
- subject {Library.new}
6
-
7
- it "supports overriding AR construction variable" do
8
- subject.should_receive(:standard_build).with("AR prog.a", "prog.a", ["sp-ar", "rcs", "prog.a", "prog.o"], ["prog.o"], env, :cache)
9
- subject.run("prog.a", ["prog.o"], :cache, env, "AR" => "sp-ar")
10
- end
11
-
12
- it "supports overriding ARCMD construction variable" do
13
- subject.should_receive(:standard_build).with("AR prog.a", "prog.a", ["special", "AR!", "prog.o"], ["prog.o"], env, :cache)
14
- subject.run("prog.a", ["prog.o"], :cache, env, "ARCMD" => ["special", "AR!", "${_SOURCES}"])
15
- end
16
- end
17
- end
18
- end
@@ -1,23 +0,0 @@
1
- module Rscons
2
- module Builders
3
- describe Object do
4
- let(:env) {Environment.new}
5
- subject {Object.new}
6
-
7
- it "supports overriding CCCMD construction variable" do
8
- cache = "cache"
9
- cache.stub(:up_to_date?) { false }
10
- cache.stub(:mkdir_p) { }
11
- cache.stub(:register_build) { }
12
- FileUtils.stub(:rm_f) { }
13
- File.stub(:exists?) { false }
14
- env.should_receive(:execute).with("CC mod.o", ["llc", "mod.c"]).and_return(true)
15
- subject.run("mod.o", ["mod.c"], cache, env, "CCCMD" => ["llc", "${_SOURCES}"])
16
- end
17
-
18
- it "raises an error when given a source file with an unknown suffix" do
19
- expect { subject.run("mod.o", ["mod.xyz"], :cache, env, {}) }.to raise_error /unknown input file type: "mod.xyz"/
20
- end
21
- end
22
- end
23
- end
@@ -1,18 +0,0 @@
1
- module Rscons
2
- module Builders
3
- describe Preprocess do
4
- let(:env) {Environment.new}
5
- subject {Preprocess.new}
6
-
7
- it "supports overriding CC construction variable" do
8
- subject.should_receive(:standard_build).with("Preprocess module.pp", "module.pp", ["my_cpp", "-E", "-o", "module.pp", "module.c"], ["module.c"], env, :cache)
9
- subject.run("module.pp", ["module.c"], :cache, env, "CC" => "my_cpp")
10
- end
11
-
12
- it "supports overriding CPP_CMD construction variable" do
13
- subject.should_receive(:standard_build).with("Preprocess module.pp", "module.pp", ["my_cpp", "module.c"], ["module.c"], env, :cache)
14
- subject.run("module.pp", ["module.c"], :cache, env, "CPP_CMD" => ["my_cpp", "${_SOURCES}"])
15
- end
16
- end
17
- end
18
- end
@@ -1,18 +0,0 @@
1
- module Rscons
2
- module Builders
3
- describe Program do
4
- let(:env) {Environment.new}
5
- subject {Program.new}
6
-
7
- it "supports overriding CC construction variable" do
8
- subject.should_receive(:standard_build).with("LD prog", "prog", ["sp-c++", "-o", "prog", "prog.o"], ["prog.o"], env, :cache)
9
- subject.run("prog", ["prog.o"], :cache, env, "CC" => "sp-c++")
10
- end
11
-
12
- it "supports overriding LDCMD construction variable" do
13
- subject.should_receive(:standard_build).with("LD prog", "prog", ["special", "LD!", "prog.o"], ["prog.o"], env, :cache)
14
- subject.run("prog", ["prog.o"], :cache, env, "LDCMD" => ["special", "LD!", "${_SOURCES}"])
15
- end
16
- end
17
- end
18
- end
@@ -1,271 +0,0 @@
1
- module Rscons
2
- describe Cache do
3
- before do
4
- File.stub(:read) { nil }
5
- end
6
-
7
- def build_from(cache)
8
- JSON.stub(:load) do
9
- cache
10
- end
11
- Cache.instance.tap do |cache|
12
- cache.send(:initialize!)
13
- end
14
- end
15
-
16
- describe "#initialize" do
17
- context "when corrupt" do
18
- it "prints a warning and defaults to an empty hash" do
19
- JSON.should_receive(:load).and_return("string")
20
- $stderr.should_receive(:puts).with(/Warning:.*was.corrupt/)
21
- c = Cache.instance
22
- c.send(:initialize!)
23
- c.instance_variable_get(:@cache).is_a?(Hash).should be_true
24
- end
25
- end
26
- end
27
-
28
- describe "#clear" do
29
- it "removes the cache file" do
30
- FileUtils.should_receive(:rm_f).with(Cache::CACHE_FILE)
31
- JSON.stub(:load) {{}}
32
- Cache.instance.clear
33
- end
34
- end
35
-
36
- describe "#write" do
37
- it "should fill in 'version' and write to file" do
38
- cache = {}
39
- fh = $stdout
40
- fh.should_receive(:puts)
41
- File.should_receive(:open).and_yield(fh)
42
- build_from(cache).write
43
- cache["version"].should == Rscons::VERSION
44
- end
45
- end
46
-
47
- describe "#up_to_date?" do
48
- empty_env = "env"
49
- before do
50
- empty_env.stub(:get_user_deps) { nil }
51
- end
52
-
53
- it "returns false when target file does not exist" do
54
- File.should_receive(:exists?).with("target").and_return(false)
55
- build_from({}).up_to_date?("target", "command", [], empty_env).should be_false
56
- end
57
-
58
- it "returns false when target is not registered in the cache" do
59
- File.should_receive(:exists?).with("target").and_return(true)
60
- build_from({}).up_to_date?("target", "command", [], empty_env).should be_false
61
- end
62
-
63
- it "returns false when the target's checksum does not match" do
64
- _cache = {"targets" => {"target" => {"checksum" => "abc"}}}
65
- cache = build_from(_cache)
66
- File.should_receive(:exists?).with("target").and_return(true)
67
- cache.should_receive(:calculate_checksum).with("target").and_return("def")
68
- cache.up_to_date?("target", "command", [], empty_env).should be_false
69
- end
70
-
71
- it "returns false when the build command has changed" do
72
- _cache = {"targets" => {"target" => {"checksum" => "abc", "command" => Digest::MD5.hexdigest("old command".inspect)}}}
73
- cache = build_from(_cache)
74
- File.should_receive(:exists?).with("target").and_return(true)
75
- cache.should_receive(:calculate_checksum).with("target").and_return("abc")
76
- cache.up_to_date?("target", "command", [], empty_env).should be_false
77
- end
78
-
79
- it "returns false when there is a new dependency" do
80
- _cache = {"targets" => {"target" => {"checksum" => "abc",
81
- "command" => Digest::MD5.hexdigest("command".inspect),
82
- "deps" => [{"fname" => "dep.1"}]}}}
83
- cache = build_from(_cache)
84
- File.should_receive(:exists?).with("target").and_return(true)
85
- cache.should_receive(:calculate_checksum).with("target").and_return("abc")
86
- cache.up_to_date?("target", "command", ["dep.1", "dep.2"], empty_env).should be_false
87
- end
88
-
89
- it "returns false when a dependency's checksum has changed" do
90
- _cache = {"targets" => {"target" => {"checksum" => "abc",
91
- "command" => Digest::MD5.hexdigest("command".inspect),
92
- "deps" => [{"fname" => "dep.1",
93
- "checksum" => "dep.1.chk"},
94
- {"fname" => "dep.2",
95
- "checksum" => "dep.2.chk"},
96
- {"fname" => "extra.dep",
97
- "checksum" => "extra.dep.chk"}],
98
- "user_deps" => []}}}
99
- cache = build_from(_cache)
100
- File.should_receive(:exists?).with("target").and_return(true)
101
- cache.should_receive(:calculate_checksum).with("target").and_return("abc")
102
- cache.should_receive(:calculate_checksum).with("dep.1").and_return("dep.1.chk")
103
- cache.should_receive(:calculate_checksum).with("dep.2").and_return("dep.2.changed")
104
- cache.up_to_date?("target", "command", ["dep.1", "dep.2"], empty_env).should be_false
105
- end
106
-
107
- it "returns false with strict_deps=true when cache has an extra dependency" do
108
- _cache = {"targets" => {"target" => {"checksum" => "abc",
109
- "command" => Digest::MD5.hexdigest("command".inspect),
110
- "deps" => [{"fname" => "dep.1",
111
- "checksum" => "dep.1.chk"},
112
- {"fname" => "dep.2",
113
- "checksum" => "dep.2.chk"},
114
- {"fname" => "extra.dep",
115
- "checksum" => "extra.dep.chk"}],
116
- "user_deps" => []}}}
117
- cache = build_from(_cache)
118
- File.should_receive(:exists?).with("target").and_return(true)
119
- cache.should_receive(:calculate_checksum).with("target").and_return("abc")
120
- cache.up_to_date?("target", "command", ["dep.1", "dep.2"], empty_env, strict_deps: true).should be_false
121
- end
122
-
123
- it "returns false when there is a new user dependency" do
124
- _cache = {"targets" => {"target" => {"checksum" => "abc",
125
- "command" => Digest::MD5.hexdigest("command".inspect),
126
- "deps" => [{"fname" => "dep.1"}],
127
- "user_deps" => []}}}
128
- cache = build_from(_cache)
129
- env = "env"
130
- env.should_receive(:get_user_deps).with("target").and_return(["file.ld"])
131
- File.should_receive(:exists?).with("target").and_return(true)
132
- cache.should_receive(:calculate_checksum).with("target").and_return("abc")
133
- cache.up_to_date?("target", "command", ["dep.1"], env).should be_false
134
- end
135
-
136
- it "returns false when a user dependency checksum has changed" do
137
- _cache = {"targets" => {"target" => {"checksum" => "abc",
138
- "command" => Digest::MD5.hexdigest("command".inspect),
139
- "deps" => [{"fname" => "dep.1",
140
- "checksum" => "dep.1.chk"},
141
- {"fname" => "dep.2",
142
- "checksum" => "dep.2.chk"},
143
- {"fname" => "extra.dep",
144
- "checksum" => "extra.dep.chk"}],
145
- "user_deps" => [{"fname" => "user.dep",
146
- "checksum" => "user.dep.chk"}]}}}
147
- cache = build_from(_cache)
148
- env = "env"
149
- env.should_receive(:get_user_deps).with("target").and_return(["user.dep"])
150
- File.should_receive(:exists?).with("target").and_return(true)
151
- cache.should_receive(:calculate_checksum).with("target").and_return("abc")
152
- cache.should_receive(:calculate_checksum).with("dep.1").and_return("dep.1.chk")
153
- cache.should_receive(:calculate_checksum).with("dep.2").and_return("dep.2.chk")
154
- cache.should_receive(:calculate_checksum).with("extra.dep").and_return("extra.dep.chk")
155
- cache.should_receive(:calculate_checksum).with("user.dep").and_return("INCORRECT")
156
- cache.up_to_date?("target", "command", ["dep.1", "dep.2"], env).should be_false
157
- end
158
-
159
- it "returns true when no condition for false is met" do
160
- _cache = {"targets" => {"target" => {"checksum" => "abc",
161
- "command" => Digest::MD5.hexdigest("command".inspect),
162
- "deps" => [{"fname" => "dep.1",
163
- "checksum" => "dep.1.chk"},
164
- {"fname" => "dep.2",
165
- "checksum" => "dep.2.chk"},
166
- {"fname" => "extra.dep",
167
- "checksum" => "extra.dep.chk"}],
168
- "user_deps" => []}}}
169
- cache = build_from(_cache)
170
- File.should_receive(:exists?).with("target").and_return(true)
171
- cache.should_receive(:calculate_checksum).with("target").and_return("abc")
172
- cache.should_receive(:calculate_checksum).with("dep.1").and_return("dep.1.chk")
173
- cache.should_receive(:calculate_checksum).with("dep.2").and_return("dep.2.chk")
174
- cache.should_receive(:calculate_checksum).with("extra.dep").and_return("extra.dep.chk")
175
- cache.up_to_date?("target", "command", ["dep.1", "dep.2"], empty_env).should be_true
176
- end
177
- end
178
-
179
- describe "#register_build" do
180
- it "stores the given information in the cache" do
181
- _cache = {}
182
- cache = build_from(_cache)
183
- env = "env"
184
- env.should_receive(:get_user_deps).with("the target").and_return(["user.dep"])
185
- cache.should_receive(:calculate_checksum).with("the target").and_return("the checksum")
186
- cache.should_receive(:calculate_checksum).with("dep 1").and_return("dep 1 checksum")
187
- cache.should_receive(:calculate_checksum).with("dep 2").and_return("dep 2 checksum")
188
- cache.should_receive(:calculate_checksum).with("user.dep").and_return("user.dep checksum")
189
- cache.register_build("the target", "the command", ["dep 1", "dep 2"], env)
190
- cached_target = cache.instance_variable_get(:@cache)["targets"]["the target"]
191
- cached_target.should_not be_nil
192
- cached_target["command"].should == Digest::MD5.hexdigest("the command".inspect)
193
- cached_target["checksum"].should == "the checksum"
194
- cached_target["deps"].should == [
195
- {"fname" => "dep 1", "checksum" => "dep 1 checksum"},
196
- {"fname" => "dep 2", "checksum" => "dep 2 checksum"},
197
- ]
198
- cached_target["user_deps"].should == [
199
- {"fname" => "user.dep", "checksum" => "user.dep checksum"},
200
- ]
201
- end
202
- end
203
-
204
- describe "#targets" do
205
- it "returns a list of targets that are cached" do
206
- cache = {"targets" => {"t1" => {}, "t2" => {}, "t3" => {}}}
207
- build_from(cache).targets.should == ["t1", "t2", "t3"]
208
- end
209
- end
210
-
211
- describe "#mkdir_p" do
212
- it "makes directories and records any created in the cache" do
213
- _cache = {}
214
- cache = build_from(_cache)
215
- File.should_receive(:exists?).with("one").and_return(true)
216
- File.should_receive(:exists?).with("one/two").and_return(false)
217
- FileUtils.should_receive(:mkdir).with("one/two")
218
- File.should_receive(:exists?).with("one/two/three").and_return(false)
219
- FileUtils.should_receive(:mkdir).with("one/two/three")
220
- File.should_receive(:exists?).with("one").and_return(true)
221
- File.should_receive(:exists?).with("one/two").and_return(true)
222
- File.should_receive(:exists?).with("one/two/four").and_return(false)
223
- FileUtils.should_receive(:mkdir).with("one/two/four")
224
- cache.mkdir_p("one/two/three")
225
- cache.mkdir_p("one\\two\\four")
226
- cache.directories.should == ["one/two", "one/two/three", "one/two/four"]
227
- end
228
-
229
- it "handles absolute paths" do
230
- _cache = {}
231
- cache = build_from(_cache)
232
- File.should_receive(:exists?).with("/one").and_return(true)
233
- File.should_receive(:exists?).with("/one/two").and_return(false)
234
- FileUtils.should_receive(:mkdir).with("/one/two")
235
- cache.mkdir_p("/one/two")
236
- cache.directories.should == ["/one/two"]
237
- end
238
- end
239
-
240
- describe "#directories" do
241
- it "returns a list of directories that are cached" do
242
- _cache = {"directories" => {"dir1" => true, "dir2" => true}}
243
- build_from(_cache).directories.should == ["dir1", "dir2"]
244
- end
245
- end
246
-
247
- describe "#lookup_checksum" do
248
- it "does not re-calculate the checksum when it is already cached" do
249
- cache = build_from({})
250
- cache.instance_variable_set(:@lookup_checksums, {"f1" => "f1.chk"})
251
- cache.should_not_receive(:calculate_checksum)
252
- cache.send(:lookup_checksum, "f1").should == "f1.chk"
253
- end
254
-
255
- it "calls calculate_checksum when the checksum is not cached" do
256
- cache = build_from({})
257
- cache.should_receive(:calculate_checksum).with("f1").and_return("ck")
258
- cache.send(:lookup_checksum, "f1").should == "ck"
259
- end
260
- end
261
-
262
- describe "#calculate_checksum" do
263
- it "calculates the MD5 of the file contents" do
264
- contents = "contents"
265
- File.should_receive(:read).with("fname", mode: "rb").and_return(contents)
266
- Digest::MD5.should_receive(:hexdigest).with(contents).and_return("the_checksum")
267
- build_from({}).send(:calculate_checksum, "fname").should == "the_checksum"
268
- end
269
- end
270
- end
271
- end
@@ -1,361 +0,0 @@
1
- module Rscons
2
- describe Environment do
3
- describe "#initialize" do
4
- it "adds the default builders when they are not excluded" do
5
- env = Environment.new
6
- env.builders.size.should be > 0
7
- env.builders.map {|name, builder| builder.is_a?(Builder)}.all?.should be_true
8
- env.builders.find {|name, builder| name == "Object"}.should_not be_nil
9
- env.builders.find {|name, builder| name == "Program"}.should_not be_nil
10
- env.builders.find {|name, builder| name == "Library"}.should_not be_nil
11
- end
12
-
13
- it "excludes the default builders with exclude_builders: :all" do
14
- env = Environment.new(exclude_builders: true)
15
- env.builders.size.should == 0
16
- end
17
-
18
- context "when a block is given" do
19
- it "yields self and invokes #process()" do
20
- env = Environment.new do |env|
21
- env.should_receive(:process)
22
- end
23
- end
24
- end
25
- end
26
-
27
- describe "#clone" do
28
- it 'should create unique copies of each construction variable' do
29
- env = Environment.new
30
- env["CPPPATH"] << "path1"
31
- env2 = env.clone
32
- env2["CPPPATH"] << "path2"
33
- env["CPPPATH"].should == ["path1"]
34
- env2["CPPPATH"].should == ["path1", "path2"]
35
- end
36
-
37
- context "when a block is given" do
38
- it "yields self and invokes #process()" do
39
- env = Environment.new
40
- env.clone do |env2|
41
- env2.should_receive(:process)
42
- end
43
- end
44
- end
45
- end
46
-
47
- describe "#add_builder" do
48
- it "adds the builder to the list of builders" do
49
- env = Environment.new(exclude_builders: true)
50
- env.builders.keys.should == []
51
- env.add_builder(Rscons::Builders::Object.new)
52
- env.builders.keys.should == ["Object"]
53
- end
54
- end
55
-
56
- describe "#get_build_fname" do
57
- context "with no build directories" do
58
- it "returns the name of the source file with suffix changed" do
59
- env = Environment.new
60
- env.get_build_fname("src/dir/file.c", ".o").should == "src/dir/file.o"
61
- env.get_build_fname("src\\dir\\other.d", ".a").should == "src/dir/other.a"
62
- env.get_build_fname("source.cc", ".o").should == "source.o"
63
- end
64
-
65
- context "with a build_root" do
66
- it "uses the build_root unless the path is absolute" do
67
- env = Environment.new
68
- env.build_root = "build/proj"
69
- env.get_build_fname("src/dir/file.c", ".o").should == "build/proj/src/dir/file.o"
70
- env.get_build_fname("/some/lib.c", ".a").should == "/some/lib.a"
71
- env.get_build_fname("C:\\abspath\\mod.cc", ".o").should == "C:/abspath/mod.o"
72
- env.get_build_fname("build\\proj\\generated.c", ".o").should == "build/proj/generated.o"
73
- env.get_build_fname("build/proj.XX", ".yy").should == "build/proj/build/proj.yy"
74
- end
75
- end
76
- end
77
-
78
- context "with build directories" do
79
- it "uses the build directories to create the output file name" do
80
- env = Environment.new
81
- env.build_dir("src", "bld")
82
- env.build_dir(%r{^libs/([^/]+)}, 'build/libs/\1')
83
- env.get_build_fname("src/input.cc", ".o").should == "bld/input.o"
84
- env.get_build_fname("libs/lib1/some/file.c", ".o").should == "build/libs/lib1/some/file.o"
85
- env.get_build_fname("libs/otherlib/otherlib.cc", ".o").should == "build/libs/otherlib/otherlib.o"
86
- env.get_build_fname("other_directory/o.d", ".a").should == "other_directory/o.a"
87
- end
88
-
89
- context "with a build_root" do
90
- it "uses the build_root unless a build directory matches or the path is absolute" do
91
- env = Environment.new
92
- env.build_dir("src", "bld")
93
- env.build_dir(%r{^libs/([^/]+)}, 'build/libs/\1')
94
- env.build_root = "bldit"
95
-
96
- env.get_build_fname("src/input.cc", ".o").should == "bld/input.o"
97
- env.get_build_fname("libs/lib1/some/file.c", ".o").should == "build/libs/lib1/some/file.o"
98
- env.get_build_fname("libs/otherlib/otherlib.cc", ".o").should == "build/libs/otherlib/otherlib.o"
99
- env.get_build_fname("other_directory/o.d", ".a").should == "bldit/other_directory/o.a"
100
- env.get_build_fname("bldit/some/mod.d", ".a").should == "bldit/some/mod.a"
101
- end
102
- end
103
- end
104
- end
105
-
106
- describe "#[]" do
107
- it "allows reading construction variables" do
108
- env = Environment.new
109
- env["CFLAGS"] = ["-g", "-Wall"]
110
- env["CFLAGS"].should == ["-g", "-Wall"]
111
- end
112
- end
113
-
114
- describe "#[]=" do
115
- it "allows writing construction variables" do
116
- env = Environment.new
117
- env["CFLAGS"] = ["-g", "-Wall"]
118
- env["CFLAGS"] -= ["-g"]
119
- env["CFLAGS"] += ["-O3"]
120
- env["CFLAGS"].should == ["-Wall", "-O3"]
121
- env["other_var"] = "val33"
122
- env["other_var"].should == "val33"
123
- end
124
- end
125
-
126
- describe "#append" do
127
- it "allows adding many construction variables at once" do
128
- env = Environment.new
129
- env["CFLAGS"] = ["-g"]
130
- env["CPPPATH"] = ["inc"]
131
- env.append("CFLAGS" => ["-Wall"], "CPPPATH" => ["include"])
132
- env["CFLAGS"].should == ["-Wall"]
133
- env["CPPPATH"].should == ["include"]
134
- end
135
- end
136
-
137
- describe "#process" do
138
- it "runs builders for all of the targets specified" do
139
- env = Environment.new
140
- env.Program("a.out", "main.c")
141
-
142
- cache = "cache"
143
- Cache.should_receive(:instance).and_return(cache)
144
- cache.should_receive(:clear_checksum_cache!)
145
- env.should_receive(:run_builder).with(anything, "a.out", ["main.c"], cache, {}).and_return(true)
146
- cache.should_receive(:write)
147
-
148
- env.process
149
- end
150
-
151
- it "builds dependent targets first" do
152
- env = Environment.new
153
- env.Program("a.out", "main.o")
154
- env.Object("main.o", "other.cc")
155
-
156
- cache = "cache"
157
- Cache.should_receive(:instance).and_return(cache)
158
- cache.should_receive(:clear_checksum_cache!)
159
- env.should_receive(:run_builder).with(anything, "main.o", ["other.cc"], cache, {}).and_return("main.o")
160
- env.should_receive(:run_builder).with(anything, "a.out", ["main.o"], cache, {}).and_return("a.out")
161
- cache.should_receive(:write)
162
-
163
- env.process
164
- end
165
-
166
- it "raises a BuildError when building fails" do
167
- env = Environment.new
168
- env.Program("a.out", "main.o")
169
- env.Object("main.o", "other.cc")
170
-
171
- cache = "cache"
172
- Cache.should_receive(:instance).and_return(cache)
173
- cache.should_receive(:clear_checksum_cache!)
174
- env.should_receive(:run_builder).with(anything, "main.o", ["other.cc"], cache, {}).and_return(false)
175
- cache.should_receive(:write)
176
-
177
- expect { env.process }.to raise_error BuildError, /Failed.to.build.main.o/
178
- end
179
-
180
- it "writes the cache when the Builder raises an exception" do
181
- env = Environment.new
182
- env.Object("module.o", "module.c")
183
-
184
- cache = "cache"
185
- Cache.should_receive(:instance).and_return(cache)
186
- cache.should_receive(:clear_checksum_cache!)
187
- env.stub(:run_builder) do |builder, target, sources, cache, vars|
188
- raise "Ruby exception thrown by builder"
189
- end
190
- cache.should_receive(:write)
191
-
192
- expect { env.process }.to raise_error RuntimeError, /Ruby exception thrown by builder/
193
- end
194
- end
195
-
196
- describe "#clear_targets" do
197
- it "resets @targets to an empty hash" do
198
- env = Environment.new
199
- env.Program("a.out", "main.o")
200
- expect(env.instance_variable_get(:@targets).keys).to eq(["a.out"])
201
-
202
- env.clear_targets
203
-
204
- expect(env.instance_variable_get(:@targets).keys).to eq([])
205
- end
206
- end
207
-
208
- describe "#build_command" do
209
- it "returns a command based on the variables in the Environment" do
210
- env = Environment.new
211
- env["path"] = ["dir1", "dir2"]
212
- env["flags"] = ["-x", "-y", "${specialflag}"]
213
- env["specialflag"] = "-z"
214
- template = ["cmd", "-I${path}", "${flags}", "${_source}", "${_dest}"]
215
- cmd = env.build_command(template, "_source" => "infile", "_dest" => "outfile")
216
- cmd.should == ["cmd", "-Idir1", "-Idir2", "-x", "-y", "-z", "infile", "outfile"]
217
- end
218
- end
219
-
220
- describe "#expand_varref" do
221
- it "returns the fully expanded variable reference" do
222
- env = Environment.new
223
- env["path"] = ["dir1", "dir2"]
224
- env["flags"] = ["-x", "-y", "${specialflag}"]
225
- env["specialflag"] = "-z"
226
- env["foo"] = {}
227
- env.expand_varref(["-p${path}", "${flags}"]).should == ["-pdir1", "-pdir2", "-x", "-y", "-z"]
228
- env.expand_varref("foo").should == "foo"
229
- expect {env.expand_varref("${foo}")}.to raise_error /expand.a.variable.reference/
230
- env.expand_varref("${specialflag}").should == "-z"
231
- env.expand_varref("${path}").should == ["dir1", "dir2"]
232
- end
233
- end
234
-
235
- describe "#execute" do
236
- context "with echo: :short" do
237
- context "with no errors" do
238
- it "prints the short description and executes the command" do
239
- env = Environment.new(echo: :short)
240
- env.should_receive(:puts).with("short desc")
241
- env.should_receive(:system).with("a", "command").and_return(true)
242
- env.execute("short desc", ["a", "command"])
243
- end
244
- end
245
-
246
- context "with errors" do
247
- it "prints the short description, executes the command, and prints the failed command line" do
248
- env = Environment.new(echo: :short)
249
- env.should_receive(:puts).with("short desc")
250
- env.should_receive(:system).with("a", "command").and_return(false)
251
- $stdout.should_receive(:write).with("Failed command was: ")
252
- env.should_receive(:puts).with("a command")
253
- env.execute("short desc", ["a", "command"])
254
- end
255
- end
256
- end
257
-
258
- context "with echo: :command" do
259
- it "prints the command executed and executes the command" do
260
- env = Environment.new(echo: :command)
261
- env.should_receive(:puts).with("a command '--arg=val with spaces'")
262
- env.should_receive(:system).with({modified: :environment}, "a", "command", "--arg=val with spaces", {opt: :val}).and_return(false)
263
- env.execute("short desc", ["a", "command", "--arg=val with spaces"], env: {modified: :environment}, options: {opt: :val})
264
- end
265
- end
266
- end
267
-
268
- describe "#method_missing" do
269
- it "calls the original method missing when the target method is not a known builder" do
270
- env = Environment.new
271
- expect {env.foobar}.to raise_error /undefined method .foobar./
272
- end
273
-
274
- it "records the target when the target method is a known builder" do
275
- env = Environment.new
276
- env.instance_variable_get(:@targets).should == {}
277
- env.Program("target", ["src1", "src2"], var: "val")
278
- target = env.instance_variable_get(:@targets)["target"]
279
- target.should_not be_nil
280
- target[:builder].is_a?(Builder).should be_true
281
- target[:sources].should == ["src1", "src2"]
282
- target[:vars].should == {var: "val"}
283
- target[:args].should == []
284
- end
285
-
286
- it "raises an error when vars is not a Hash" do
287
- env = Environment.new
288
- expect { env.Program("a.out", "main.c", "other") }.to raise_error /Unexpected construction variable set/
289
- end
290
- end
291
-
292
- describe "#depends" do
293
- it "records the given dependencies in @user_deps" do
294
- env = Environment.new
295
- env.depends("foo", "bar", "baz")
296
- env.instance_variable_get(:@user_deps).should == {"foo" => ["bar", "baz"]}
297
- end
298
- it "records user dependencies only once" do
299
- env = Environment.new
300
- env.instance_variable_set(:@user_deps, {"foo" => ["bar"]})
301
- env.depends("foo", "bar", "baz")
302
- env.instance_variable_get(:@user_deps).should == {"foo" => ["bar", "baz"]}
303
- end
304
- end
305
-
306
- describe "#build_sources" do
307
- class ABuilder < Builder
308
- def produces?(target, source, env)
309
- target =~ /\.ab_out$/ and source =~ /\.ab_in$/
310
- end
311
- end
312
-
313
- it "finds and invokes a builder to produce output files with the requested suffixes" do
314
- cache = "cache"
315
- env = Environment.new
316
- env.add_builder(ABuilder.new)
317
- env.builders["Object"].should_receive(:run).with("mod.o", ["mod.c"], cache, env, anything).and_return("mod.o")
318
- env.builders["ABuilder"].should_receive(:run).with("mod2.ab_out", ["mod2.ab_in"], cache, env, anything).and_return("mod2.ab_out")
319
- env.build_sources(["precompiled.o", "mod.c", "mod2.ab_in"], [".o", ".ab_out"], cache, {}).should == ["precompiled.o", "mod.o", "mod2.ab_out"]
320
- end
321
- end
322
-
323
- describe "#run_builder" do
324
- it "modifies the construction variables using given build hooks and invokes the builder" do
325
- env = Environment.new
326
- env.add_build_hook do |build_op|
327
- if build_op[:sources].first =~ %r{src/special}
328
- build_op[:vars]["CFLAGS"] += ["-O3", "-DSPECIAL"]
329
- end
330
- end
331
- env.builders["Object"].stub(:run) do |target, sources, cache, env, vars|
332
- vars["CFLAGS"].should == []
333
- end
334
- env.run_builder(env.builders["Object"], "build/normal/module.o", ["src/normal/module.c"], "cache", {})
335
- env.builders["Object"].stub(:run) do |target, sources, cache, env, vars|
336
- vars["CFLAGS"].should == ["-O3", "-DSPECIAL"]
337
- end
338
- env.run_builder(env.builders["Object"], "build/special/module.o", ["src/special/module.c"], "cache", {})
339
- end
340
- end
341
-
342
- describe ".parse_makefile_deps" do
343
- it 'handles dependencies on one line' do
344
- File.should_receive(:read).with('makefile').and_return(<<EOS)
345
- module.o: source.cc
346
- EOS
347
- Environment.parse_makefile_deps('makefile', 'module.o').should == ['source.cc']
348
- end
349
-
350
- it 'handles dependencies split across many lines' do
351
- File.should_receive(:read).with('makefile').and_return(<<EOS)
352
- module.o: module.c \\
353
- module.h \\
354
- other.h
355
- EOS
356
- Environment.parse_makefile_deps('makefile', 'module.o').should == [
357
- 'module.c', 'module.h', 'other.h']
358
- end
359
- end
360
- end
361
- end