vanagon 0.4.1 → 0.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.
@@ -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