vanagon 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,29 @@
1
+ class Vanagon
2
+ module Utilities
3
+ module ShellUtilities
4
+ module_function
5
+
6
+ # join a combination of strings and arrays of strings into a single command
7
+ # joined with '&&'
8
+ #
9
+ # @param commands [Array<String, Array<String>>]
10
+ # @return [String]
11
+ def andand(*commands)
12
+ cmdjoin(commands, " && ")
13
+ end
14
+
15
+ # join a combination of strings and arrays of strings into a single command
16
+ # joined with '&&' and broken up with newlines after each '&&'
17
+ #
18
+ # @param commands [Array<String, Array<String>>]
19
+ # @return [String]
20
+ def andand_multiline(*commands)
21
+ cmdjoin(commands, " && \\\n")
22
+ end
23
+
24
+ def cmdjoin(commands, sep)
25
+ commands.map { |o| Array(o) }.flatten.join(sep)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,50 @@
1
+ require 'makefile'
2
+
3
+ describe Makefile::Rule do
4
+ describe "a rule with no dependencies and an empty recipe" do
5
+ subject { described_class.new("empty") }
6
+
7
+ it "creates an empty rule" do
8
+ expect(subject.format).to eq "empty:\n"
9
+ end
10
+ end
11
+
12
+ describe "a rule with no dependencies and a simple recipe" do
13
+ subject { described_class.new("simple", recipe: ["touch simple"]) }
14
+
15
+ it "creates the rule with the recipe" do
16
+ expect(subject.format).to eq "simple:\n\ttouch simple\n"
17
+ end
18
+ end
19
+
20
+ describe "a rule with dependencies and no recipe" do
21
+ subject { described_class.new("depends", dependencies: ["mydeps"]) }
22
+
23
+ it "creates the rule with the recipe" do
24
+ expect(subject.format).to eq "depends: mydeps\n"
25
+ end
26
+ end
27
+
28
+ describe "a rule with dependencies and a recipe" do
29
+ subject { described_class.new("deluxe", recipe: ["touch deluxe"], dependencies: ["mydeps"]) }
30
+
31
+ it "creates the rule with the recipe" do
32
+ expect(subject.format).to eq "deluxe: mydeps\n\ttouch deluxe\n"
33
+ end
34
+ end
35
+
36
+ describe "a rule with a multiline recipe" do
37
+ subject do
38
+ described_class.new("multiline") do |rule|
39
+ rule.recipe = [
40
+ "[ -d build ] || mkdir -p build",
41
+ "cd build &&\ncmake .. &&\nmake &&\nmake install"
42
+ ]
43
+ end
44
+ end
45
+
46
+ it "inserts tabs after each newline in the recipe" do
47
+ expect(subject.format).to eq "multiline:\n\t[ -d build ] || mkdir -p build\n\tcd build &&\n\tcmake .. &&\n\tmake &&\n\tmake install\n"
48
+ end
49
+ end
50
+ end
@@ -105,6 +105,21 @@ end" }
105
105
  end
106
106
  end
107
107
 
108
+ describe '#check' do
109
+ it 'sets check to the value if check is empty' do
110
+ comp = Vanagon::Component::DSL.new('check-test', {}, {})
111
+ comp.check { './check' }
112
+ expect(comp._component.check).to eq(['./check'])
113
+ end
114
+
115
+ it 'appends to the existing check if not empty' do
116
+ comp = Vanagon::Component::DSL.new('check-test', {}, {})
117
+ comp.check { 'make test' }
118
+ comp.check { 'make cpplint' }
119
+ expect(comp._component.check).to eq(['make test', 'make cpplint'])
120
+ end
121
+ end
122
+
108
123
  describe '#install' do
109
124
  it 'sets install to the value if install is empty' do
110
125
  comp = Vanagon::Component::DSL.new('install-test', {}, {})
@@ -136,8 +151,37 @@ end" }
136
151
  comp.apply_patch('patch_file1', fuzz: 12, strip: 1000000)
137
152
  expect(comp._component.patches.count).to eq 1
138
153
  expect(comp._component.patches.first.path).to eq 'patch_file1'
139
- expect(comp._component.patches.first.fuzz).to eq '12'
140
- expect(comp._component.patches.first.strip).to eq '1000000'
154
+ expect(comp._component.patches.first.fuzz).to eq 12
155
+ expect(comp._component.patches.first.strip).to eq 1000000
156
+ end
157
+
158
+ it 'can specify a directory where the patch should be applied' do
159
+ comp = Vanagon::Component::DSL.new('patch-test', {}, {})
160
+ comp.apply_patch('patch_file1', destination: 'random/install/directory')
161
+ expect(comp._component.patches.count).to eq 1
162
+ expect(comp._component.patches.first.path).to eq 'patch_file1'
163
+ expect(comp._component.patches.first.destination).to eq 'random/install/directory'
164
+ end
165
+
166
+ it 'can specify when to try to apply the patch' do
167
+ comp = Vanagon::Component::DSL.new('patch-test', {}, {})
168
+ comp.apply_patch('patch_file1', after: 'install')
169
+ expect(comp._component.patches.count).to eq 1
170
+ expect(comp._component.patches.first.path).to eq 'patch_file1'
171
+ expect(comp._component.patches.first.after).to eq 'install'
172
+ end
173
+
174
+ it 'will default the patch timing to after the source is unpacked' do
175
+ comp = Vanagon::Component::DSL.new('patch-test', {}, {})
176
+ comp.apply_patch('patch_file1')
177
+ expect(comp._component.patches.count).to eq 1
178
+ expect(comp._component.patches.first.path).to eq 'patch_file1'
179
+ expect(comp._component.patches.first.after).to eq 'unpack'
180
+ end
181
+
182
+ it 'will fail if the user wants to install the patch at an unsupported step' do
183
+ comp = Vanagon::Component::DSL.new('patch-test', {}, {})
184
+ expect { comp.apply_patch('patch_file1', after: 'delivery') }.to raise_error(Vanagon::Error)
141
185
  end
142
186
  end
143
187
 
@@ -484,6 +528,21 @@ end" }
484
528
  end
485
529
  end
486
530
 
531
+ describe "#build_dir" do
532
+ it "sets the build_dir when given a relative path" do
533
+ comp = Vanagon::Component::DSL.new('build-dir-test', {}, platform)
534
+ comp.build_dir("build")
535
+ expect(comp._component.build_dir).to eq("build")
536
+ end
537
+
538
+ it "raises an error when given an absolute path" do
539
+ comp = Vanagon::Component::DSL.new('build-dir-test', {}, platform)
540
+ expect {
541
+ comp.build_dir("/build")
542
+ }.to raise_error(Vanagon::Error, %r[build_dir should be a relative path, but '/build' looks to be absolute\.])
543
+ end
544
+ end
545
+
487
546
  describe '#link' do
488
547
  it 'adds the correct command to the install for the component' do
489
548
  comp = Vanagon::Component::DSL.new('link-test', {}, platform)
@@ -0,0 +1,302 @@
1
+ require 'vanagon/component'
2
+ require 'vanagon/component/dsl'
3
+ require 'vanagon/component/rules'
4
+ require 'vanagon/platform/osx'
5
+ require 'vanagon/project'
6
+ require 'vanagon/patch'
7
+ require 'ostruct'
8
+
9
+ RSpec.shared_examples "a rule that touches the target file" do
10
+ it "touches the target file as the last step of the recipe" do
11
+ expect(rule.recipe.last).to eq "touch #{rule.target}"
12
+ end
13
+ end
14
+
15
+ describe Vanagon::Component::Rules do
16
+ let(:platform) do
17
+ OpenStruct.new(:patch => "/usr/bin/patch", :make => '/usr/bin/make')
18
+ end
19
+
20
+ let(:project) do
21
+ Vanagon::Project.new("cpp-project", platform)
22
+ end
23
+
24
+ let(:component) do
25
+ Vanagon::Component.new("leatherman", {}, platform).tap do |c|
26
+ c.dirname = "/foo/bar"
27
+ end
28
+ end
29
+
30
+ subject { described_class.new(component, project, platform) }
31
+
32
+ describe "the component rule" do
33
+ it "depends on the component-install rule" do
34
+ rule = subject.component_rule
35
+ expect(rule.dependencies).to eq(["leatherman-install"])
36
+ end
37
+ end
38
+
39
+ describe "the unpack rule" do
40
+ let(:rule) { subject.unpack_rule }
41
+
42
+ it { expect(rule.dependencies).to eq(["file-list-before-build"]) }
43
+
44
+ it "extracts the source" do
45
+ component.extract_with = "/usr/bin/tar"
46
+ expect(rule.recipe.first).to eq ": && /usr/bin/tar"
47
+ end
48
+
49
+ it "sets environment variables before running the unpack steps" do
50
+ component.extract_with = "/usr/bin/tar"
51
+ component.environment = {"PATH" => "/opt/pl-build-tools/bin:$$PATH"}
52
+ expect(rule.recipe.first).to eq(
53
+ [
54
+ "export PATH=\"/opt/pl-build-tools/bin:$$PATH\"",
55
+ "/usr/bin/tar"
56
+ ].join(" && ")
57
+ )
58
+ end
59
+
60
+ it_behaves_like "a rule that touches the target file"
61
+ end
62
+
63
+ describe "the patch rule" do
64
+ let(:rule) { subject.patch_rule }
65
+
66
+ it { expect(rule.dependencies).to eq(["leatherman-unpack"]) }
67
+
68
+ it "does nothing when there are no patches" do
69
+ expect(rule.recipe.size).to eq 1
70
+ end
71
+
72
+ it "applies each listed patch in order when patches are set" do
73
+ component.patches = [
74
+ Vanagon::Patch.new('/foo/patch0', '1', '0', 'unpack', '/foo/bar'),
75
+ Vanagon::Patch.new('/foo/patch1', '2', '1', 'unpack', '/foo/bar'),
76
+ Vanagon::Patch.new('/foo/postinstall/patch1', '2', '1', 'install', '/foo/bar')
77
+ ]
78
+ expect(rule.recipe.first).to eq(
79
+ [
80
+ "cd /foo/bar",
81
+ "/usr/bin/patch --strip=1 --fuzz=0 --ignore-whitespace < $(workdir)/patches/patch0",
82
+ "/usr/bin/patch --strip=2 --fuzz=1 --ignore-whitespace < $(workdir)/patches/patch1"
83
+ ].join(" && \\\n")
84
+ )
85
+ end
86
+
87
+ it_behaves_like "a rule that touches the target file"
88
+ end
89
+
90
+ describe "the configure rule" do
91
+ let(:rule) { subject.configure_rule }
92
+
93
+ # TODO: cross-component dependencies
94
+ it { expect(rule.dependencies).to eq(['leatherman-patch']) }
95
+
96
+ describe "when a build directory is set" do
97
+ before do
98
+ component.build_dir = "build"
99
+ end
100
+
101
+ it "creates the build directory" do
102
+ expect(rule.recipe.first).to eq("[ -d /foo/bar/build ] || mkdir -p /foo/bar/build")
103
+ end
104
+ end
105
+
106
+ it "runs all of the configure commands when given" do
107
+ component.configure = ["./configure", "cmake .."]
108
+ expect(rule.recipe[1]).to eq(
109
+ [
110
+ "cd /foo/bar",
111
+ ":",
112
+ "./configure",
113
+ "cmake .."
114
+ ].join(" && \\\n")
115
+ )
116
+ end
117
+
118
+ it "sets environment variables before running the configure steps" do
119
+ component.configure = ["./configure", "cmake .."]
120
+ component.environment = {"PATH" => "/opt/pl-build-tools/bin:$$PATH"}
121
+ expect(rule.recipe[1]).to eq(
122
+ [
123
+ "cd /foo/bar",
124
+ "export PATH=\"/opt/pl-build-tools/bin:$$PATH\"",
125
+ "./configure",
126
+ "cmake .."
127
+ ].join(" && \\\n")
128
+ )
129
+ end
130
+
131
+ it_behaves_like "a rule that touches the target file"
132
+ end
133
+
134
+ describe "the build rule" do
135
+ let(:rule) { subject.build_rule }
136
+
137
+ it { expect(rule.dependencies).to eq(['leatherman-configure']) }
138
+
139
+ it "does nothing when the build step is empty" do
140
+ expect(rule.recipe.size).to eq 1
141
+ end
142
+
143
+ it "runs all of the build commands when given" do
144
+ component.build = ["make", "make test"]
145
+ expect(rule.recipe.first).to eq(
146
+ [
147
+ "cd /foo/bar",
148
+ ":",
149
+ "make",
150
+ "make test",
151
+ ].join(" && \\\n")
152
+ )
153
+ end
154
+
155
+ it "sets environment variables before running the build steps" do
156
+ component.build = ["make", "make test"]
157
+ component.environment = {"PATH" => "/opt/pl-build-tools/bin:$$PATH"}
158
+ expect(rule.recipe.first).to eq(
159
+ [
160
+ "cd /foo/bar",
161
+ "export PATH=\"/opt/pl-build-tools/bin:$$PATH\"",
162
+ "make",
163
+ "make test"
164
+ ].join(" && \\\n")
165
+ )
166
+ end
167
+
168
+ it_behaves_like "a rule that touches the target file"
169
+ end
170
+
171
+ describe "the check rule" do
172
+ let(:rule) { subject.check_rule }
173
+
174
+ it { expect(rule.dependencies).to eq(['leatherman-build']) }
175
+
176
+ it "does nothing when the check step is empty" do
177
+ expect(rule.recipe.size).to eq 1
178
+ end
179
+
180
+ it "does nothing when the project skipcheck flag is set" do
181
+ component.check = ["make cpplint", "make test"]
182
+ project.settings[:skipcheck] = true
183
+ expect(rule.recipe.size).to eq 1
184
+ end
185
+
186
+ it "runs all of the check commands when given" do
187
+ component.check = ["make cpplint", "make test"]
188
+ expect(rule.recipe.first).to eq(
189
+ [
190
+ "cd /foo/bar",
191
+ ":",
192
+ "make cpplint",
193
+ "make test",
194
+ ].join(" && \\\n")
195
+ )
196
+ end
197
+
198
+ it "sets environment variables before running the check steps" do
199
+ component.check = ["make cpplint", "make test"]
200
+ component.environment = {"PATH" => "/opt/pl-build-tools/bin:$$PATH"}
201
+ expect(rule.recipe.first).to eq(
202
+ [
203
+ "cd /foo/bar",
204
+ "export PATH=\"/opt/pl-build-tools/bin:$$PATH\"",
205
+ "make cpplint",
206
+ "make test"
207
+ ].join(" && \\\n")
208
+ )
209
+ end
210
+
211
+ it_behaves_like "a rule that touches the target file"
212
+ end
213
+
214
+ describe "the install rule" do
215
+ let(:rule) { subject.install_rule }
216
+
217
+ it { expect(rule.dependencies).to eq(['leatherman-check']) }
218
+
219
+ it "does nothing when the install step is empty" do
220
+ expect(rule.recipe.size).to eq 1
221
+ end
222
+
223
+ it "runs all of the install commands when given" do
224
+ component.install = ["make install", "make reallyinstall"]
225
+ expect(rule.recipe.first).to eq(
226
+ [
227
+ "cd /foo/bar",
228
+ ":",
229
+ "make install",
230
+ "make reallyinstall",
231
+ ].join(" && \\\n")
232
+ )
233
+ end
234
+
235
+ it "sets environment variables before running the install steps" do
236
+ component.install = ["make install", "make reallyinstall"]
237
+ component.environment = {"PATH" => "/opt/pl-build-tools/bin:$$PATH"}
238
+ expect(rule.recipe.first).to eq(
239
+ [
240
+ "cd /foo/bar",
241
+ "export PATH=\"/opt/pl-build-tools/bin:$$PATH\"",
242
+ "make install",
243
+ "make reallyinstall"
244
+ ].join(" && \\\n")
245
+ )
246
+ end
247
+
248
+ it "applies any after-install patches" do
249
+ component.install = ["make install"]
250
+ component.patches = [
251
+ Vanagon::Patch.new('/foo/patch0', 1, 0, 'unpack', '/foo/bar'),
252
+ Vanagon::Patch.new('/foo/postinstall/patch0', 3, 9, 'install', '/foo/baz'),
253
+ Vanagon::Patch.new('/foo/postinstall/patch1', 4, 10, 'install', '/foo/quux'),
254
+ ]
255
+
256
+ expect(rule.recipe[1]).to eq("cd /foo/baz && /usr/bin/patch --strip=3 --fuzz=9 --ignore-whitespace < $(workdir)/patches/patch0")
257
+ expect(rule.recipe[2]).to eq("cd /foo/quux && /usr/bin/patch --strip=4 --fuzz=10 --ignore-whitespace < $(workdir)/patches/patch1")
258
+ end
259
+
260
+ it_behaves_like "a rule that touches the target file"
261
+ end
262
+
263
+ describe "the cleanup rule" do
264
+ let(:rule) { subject.cleanup_rule }
265
+
266
+ it { expect(rule.dependencies).to eq(['leatherman-install']) }
267
+
268
+ it "runs the component source cleanup step" do
269
+ component.cleanup_source = "rm -rf leatherman"
270
+ expect(rule.recipe.first).to eq "rm -rf leatherman"
271
+ end
272
+
273
+ it_behaves_like "a rule that touches the target file"
274
+ end
275
+
276
+ describe "the clean rule" do
277
+ let(:rule) { subject.clean_rule }
278
+
279
+ it { expect(rule.dependencies).to be_empty }
280
+
281
+ it "runs a `make clean` in the build dir" do
282
+ expect(rule.recipe.first).to eq '[ -d /foo/bar ] && cd /foo/bar && /usr/bin/make clean'
283
+ end
284
+
285
+ it "remotes the touch files for the configure, build, and install steps" do
286
+ %w[configure build install].each_with_index do |type, i|
287
+ touchfile = "leatherman-#{type}"
288
+ expect(rule.recipe[i + 1]).to eq "[ -e #{touchfile} ] && rm #{touchfile}"
289
+ end
290
+ end
291
+ end
292
+
293
+ describe "the clobber rule" do
294
+ let(:rule) { subject.clobber_rule }
295
+
296
+ it { expect(rule.dependencies).to eq(['leatherman-clean']) }
297
+
298
+ it "removes the source directory and unpack touchfile" do
299
+ expect(rule.recipe).to eq(["[ -d /foo/bar ] && rm -r /foo/bar", "[ -e leatherman-unpack ] && rm leatherman-unpack"])
300
+ end
301
+ end
302
+ end