rscons 1.4.3 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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