build-graph 2.1.0 → 2.2.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.
@@ -1,172 +0,0 @@
1
- #!/usr/bin/env rspec
2
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy
5
- # of this software and associated documentation files (the "Software"), to deal
6
- # in the Software without restriction, including without limitation the rights
7
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- # copies of the Software, and to permit persons to whom the Software is
9
- # furnished to do so, subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in
12
- # all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
- # THE SOFTWARE.
21
-
22
- require_relative 'process_graph'
23
-
24
- RSpec.describe Build::Graph do
25
- let(:group) {Process::Group.new}
26
-
27
- it "shouldn't update mtime" do
28
- test_glob = Build::Files::Glob.new(__dir__, "*.rb")
29
- listing_output = Build::Files::Paths.directory(__dir__, ["listing.txt"])
30
-
31
- FileUtils.rm_f listing_output.to_a
32
-
33
- walker = Build::Graph::Walker.for(ProcessTask, group)
34
-
35
- top = ProcessNode.top do
36
- process test_glob, listing_output do
37
- run("ls", "-la", *inputs, :out => outputs.first.for_writing)
38
- end
39
- end
40
-
41
- group.wait do
42
- walker.update(top)
43
- end
44
-
45
- first_modified_time = listing_output.first.modified_time
46
-
47
- group.wait do
48
- walker.update(top)
49
- end
50
-
51
- # The output file shouldn't have been changed because already exists and the input files haven't changed either:
52
- second_modified_time = listing_output.first.modified_time
53
-
54
- # The granularity of mtime on some systems is a bit weird:
55
- expect(second_modified_time.to_f).to be_within(0.001).of(first_modified_time.to_f)
56
-
57
- FileUtils.rm_f listing_output.to_a
58
- walker.monitor.update(listing_output.roots)
59
-
60
- # The granularity of modification times isn't that great, so we use >= below.
61
- # sleep 1
62
-
63
- group.wait do
64
- walker.update(top)
65
- end
66
-
67
- expect(listing_output.first.modified_time).to be >= first_modified_time
68
-
69
- FileUtils.rm_f listing_output.to_a
70
- end
71
-
72
- it "should compile program and respond to changes in source code" do
73
- program_root = Build::Files::Path.join(__dir__, "program")
74
- code_glob = Build::Files::Glob.new(program_root, "*.cpp")
75
- program_path = Build::Files::Path.join(program_root, "dictionary-sort")
76
-
77
- walker = Build::Graph::Walker.for(ProcessTask, group)
78
-
79
- #FileUtils.touch(code_glob.first)
80
-
81
- top = ProcessNode.top do
82
- process code_glob, program_path do
83
- object_files = inputs.with(extension: ".o") do |input_path, output_path|
84
- depfile_path = input_path + ".d"
85
-
86
- dependencies = Build::Files::Paths.new(input_path)
87
-
88
- if File.exist? depfile_path
89
- depfile = Build::Makefile.load_file(depfile_path)
90
-
91
- dependencies = depfile[output_path] || dependencies
92
- end
93
-
94
- process dependencies, output_path do
95
- run("clang++", "-MMD", "-O3",
96
- "-o", output_path.shortest_path(input_path.root),
97
- "-c", input_path.relative_path, "-std=c++11",
98
- chdir: input_path.root
99
- )
100
- end
101
- end
102
-
103
- process object_files, program_path do
104
- run("clang++", "-O3", "-o", program_path, *object_files.to_a, "-lm", "-pthread")
105
- end
106
- end
107
-
108
- process program_path do
109
- run("./" + program_path.relative_path, chdir: program_path.root)
110
- end
111
- end
112
-
113
- group.wait do
114
- walker.update(top)
115
- end
116
-
117
- expect(program_path).to be_exist
118
- expect(code_glob.first.modified_time).to be <= program_path.modified_time
119
- end
120
-
121
- it "should copy files incrementally" do
122
- program_root = Build::Files::Path.join(__dir__, "program")
123
- files = Build::Files::Glob.new(program_root, "*.cpp")
124
- destination = Build::Files::Path.new(__dir__) + "tmp"
125
-
126
- walker = Build::Graph::Walker.for(ProcessTask, group)
127
-
128
- top = ProcessNode.top files do
129
- mkpath destination
130
-
131
- inputs.each do |source_path|
132
- destination_path = source_path.rebase(destination)
133
-
134
- process source_path, destination_path do
135
- $stderr.puts "Copying #{inputs.first} -> #{outputs.first}"
136
- install inputs.first, outputs.first
137
- end
138
- end
139
- end
140
-
141
- mutex = Mutex.new
142
- files_deleted = false
143
-
144
- thread = Thread.new do
145
- sleep 1
146
-
147
- mutex.synchronize do
148
- destination.glob("*.cpp").delete
149
-
150
- files_deleted = true
151
- end
152
- end
153
-
154
- walker.run do
155
- mutex.synchronize do
156
- group.wait do
157
- walker.update(top)
158
- end
159
- end
160
-
161
- break if files_deleted
162
- end
163
-
164
- thread.join
165
-
166
- expect(destination).to be_exist
167
- # This line failed, may still be a race condition:
168
- expect(destination.glob("*.cpp").count).to be == 2
169
-
170
- destination.delete
171
- end
172
- end
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env rspec
2
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy
5
- # of this software and associated documentation files (the "Software"), to deal
6
- # in the Software without restriction, including without limitation the rights
7
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- # copies of the Software, and to permit persons to whom the Software is
9
- # furnished to do so, subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in
12
- # all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
- # THE SOFTWARE.
21
-
22
- require 'build/graph/walker'
23
- require 'build/graph/task'
24
- require 'build/files'
25
-
26
- RSpec.describe Build::Graph::Walker do
27
- it "should inherit children outputs", :focus do
28
- test_glob = Build::Files::Glob.new(__dir__, "*.rb")
29
- listing_output = Build::Files::Paths.directory(__dir__, ["listing.txt"])
30
-
31
- node_a = Build::Graph::Node.new(Build::Files::Paths::NONE, :inherit)
32
- node_b = Build::Graph::Node.new(test_glob, listing_output)
33
-
34
- walker = Build::Graph::Walker.new do |walker, node|
35
- task = Build::Graph::Task.new(walker, node)
36
-
37
- task.visit do
38
- if node == node_a
39
- task.invoke(node_b)
40
- end
41
- end
42
- end
43
-
44
- walker.update([node_a])
45
-
46
- task_a = walker.tasks[node_a]
47
- task_b = walker.tasks[node_b]
48
-
49
- expect(task_a.outputs.to_a).to be == task_b.outputs.to_a
50
- end
51
- end
@@ -1,80 +0,0 @@
1
- #!/usr/bin/env rspec
2
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy
5
- # of this software and associated documentation files (the "Software"), to deal
6
- # in the Software without restriction, including without limitation the rights
7
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- # copies of the Software, and to permit persons to whom the Software is
9
- # furnished to do so, subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in
12
- # all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
- # THE SOFTWARE.
21
-
22
- require 'build/graph/node'
23
- require 'build/files/glob'
24
- require 'build/files/system'
25
-
26
- RSpec.describe Build::Graph::Node do
27
- include Build::Graph
28
- include Build::Files
29
-
30
- let(:test_glob) {Build::Files::Glob.new(__dir__, "*.rb")}
31
- let(:listing_output) {Build::Files::Paths.directory(__dir__, ["listing.txt"])}
32
-
33
- it "should be unique" do
34
- node_a = Build::Graph::Node.new(test_glob, listing_output)
35
- node_b = Build::Graph::Node.new(listing_output, Build::Files::Paths::NONE)
36
-
37
- expect(node_a).to be_eql node_a
38
- expect(node_a).to_not be_eql node_b
39
-
40
- node_c = Build::Graph::Node.new(test_glob, listing_output)
41
-
42
- expect(node_a).to be_eql node_c
43
- end
44
-
45
- it "should be dirty" do
46
- node_a = Build::Graph::Node.new(test_glob, listing_output)
47
-
48
- expect(node_a.dirty?).to be true
49
- end
50
-
51
- it "should be clean" do
52
- listing_output.first.touch
53
-
54
- node_a = Build::Graph::Node.new(test_glob, listing_output)
55
-
56
- expect(node_a.dirty?).to be false
57
-
58
- listing_output.first.delete
59
- end
60
-
61
- it "should be dirty if input files are missing" do
62
- input = Build::Files::Paths.directory(__dir__, ["missing-input.txt"])
63
- output = Build::Files::Glob.new(__dir__, "*.rb")
64
-
65
- node = Build::Graph::Node.new(input, output)
66
-
67
- expect(node.missing?).to be true
68
- expect(node.dirty?).to be true
69
- end
70
-
71
- it "should be dirty if output files are missing" do
72
- input = Build::Files::Glob.new(__dir__, "*.rb")
73
- output = Build::Files::Paths.directory(__dir__, ["missing-output.txt"])
74
-
75
- node = Build::Graph::Node.new(input, output)
76
-
77
- expect(node.missing?).to be true
78
- expect(node.dirty?).to be true
79
- end
80
- end
@@ -1,89 +0,0 @@
1
-
2
- require 'process/group'
3
- require 'build/files'
4
- require 'build/graph'
5
-
6
- require 'console/event/spawn'
7
-
8
- class ProcessNode < Build::Graph::Node
9
- def initialize(inputs, outputs, block, title: nil)
10
- super(inputs, outputs)
11
-
12
- if title
13
- @title = title
14
- else
15
- @title = block.source_location
16
- end
17
-
18
- @block = block
19
- end
20
-
21
- def == other
22
- super and
23
- @title == other.title and
24
- @block == other.block
25
- end
26
-
27
- def hash
28
- super ^ @title.hash ^ @block.hash
29
- end
30
-
31
- def evaluate(context)
32
- context.instance_eval(&@block)
33
- end
34
-
35
- attr :title
36
- end
37
-
38
- class ProcessTask < Build::Graph::Task
39
- def initialize(walker, node, group)
40
- super(walker, node)
41
-
42
- @group = group
43
- end
44
-
45
- def process(inputs, outputs = :inherit, **options, &block)
46
- inputs = Build::Files::List.coerce(inputs)
47
- outputs = Build::Files::List.coerce(outputs) unless outputs.kind_of? Symbol
48
-
49
- node = ProcessNode.new(inputs, outputs, block, **options)
50
-
51
- self.invoke(node)
52
- end
53
-
54
- def wet?
55
- @node.dirty?
56
- end
57
-
58
- def run(*arguments)
59
- if wet?
60
- @walker.logger.debug(self) {Console::Event::Spawn.for(*arguments)}
61
-
62
- status = @group.spawn(*arguments)
63
-
64
- if status != 0
65
- raise CommandError.new(status)
66
- end
67
- end
68
- end
69
-
70
- def mkpath(*args)
71
- return unless wet?
72
-
73
- FileUtils.mkpath(*args)
74
- end
75
-
76
- def install(*args)
77
- return unless wet?
78
-
79
- FileUtils.install(*args)
80
- end
81
-
82
- # This function is called to finish the invocation of the task within the graph.
83
- # There are two possible ways this function can generally proceed.
84
- # 1/ The node this task is running for is clean, and thus no actual processing needs to take place, but children should probably be executed.
85
- # 2/ The node this task is running for is dirty, and the execution of commands should work as expected.
86
- def update
87
- @node.evaluate(self)
88
- end
89
- end
@@ -1,72 +0,0 @@
1
- //
2
- // Benchmark.cpp
3
- // DictionarySort
4
- //
5
- // Created by Samuel Williams on 2/11/11.
6
- // Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
7
- //
8
-
9
- #include "Benchmark.h"
10
-
11
- #include <sys/time.h>
12
-
13
- // A timer class for quickly checking the wall-clock performance of code.
14
- namespace Benchmark
15
- {
16
- static TimeT system_time () {
17
- static struct timeval t;
18
- gettimeofday (&t, (struct timezone*)0);
19
- return ((TimeT)t.tv_sec) + ((TimeT)t.tv_usec / 1000000.0);
20
- }
21
-
22
- WallTime::WallTime () {
23
- this->reset();
24
- }
25
-
26
- void WallTime::reset () {
27
- this->_last = system_time();
28
- this->_total = 0.0;
29
- }
30
-
31
- TimeT WallTime::total () const {
32
- TimeT current = system_time();
33
- this->_total += current - this->_last;
34
- this->_last = current;
35
- return this->_total;
36
- }
37
-
38
- ProcessorTime::ProcessorTime()
39
- {
40
- this->reset();
41
- }
42
-
43
- void ProcessorTime::reset ()
44
- {
45
- this->_last = std::clock();
46
- this->_total = 0;
47
- }
48
-
49
- TimeT ProcessorTime::total () const
50
- {
51
- std::clock_t current = std::clock();
52
- this->_total += std::clock() - this->_last;
53
- this->_last = current;
54
-
55
- return TimeT(this->_total) / TimeT(CLOCKS_PER_SEC);
56
- }
57
-
58
- Timer::Timer()
59
- {
60
- }
61
-
62
- void Timer::reset ()
63
- {
64
- _wall_time.reset();
65
- _processor_time.reset();
66
- }
67
-
68
- Timer::Sample Timer::sample() const
69
- {
70
- return {_wall_time.total(), _processor_time.total()};
71
- }
72
- }
@@ -1,65 +0,0 @@
1
- //
2
- // Benchmark.h
3
- // DictionarySort
4
- //
5
- // Created by Samuel Williams on 2/11/11.
6
- // Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
7
- //
8
-
9
- #pragma once
10
-
11
- #include <ctime>
12
-
13
- // A timer class for quickly checking the wall-clock performance of code.
14
- namespace Benchmark
15
- {
16
- typedef double TimeT;
17
-
18
- class WallTime {
19
- protected:
20
- mutable TimeT _last, _total;
21
-
22
- public:
23
- WallTime ();
24
-
25
- void reset ();
26
- TimeT total () const;
27
- };
28
-
29
- class ProcessorTime {
30
- protected:
31
- mutable std::clock_t _last, _total;
32
-
33
- public:
34
- ProcessorTime();
35
-
36
- void reset ();
37
- TimeT total () const;
38
- };
39
-
40
- class Timer {
41
- protected:
42
- WallTime _wall_time;
43
- ProcessorTime _processor_time;
44
-
45
- public:
46
- Timer();
47
-
48
- const WallTime & wall_time() const { return _wall_time; }
49
- const ProcessorTime & processor_time() const { return _processor_time; }
50
-
51
- void reset ();
52
-
53
- struct Sample {
54
- TimeT wall_time_total;
55
- TimeT processor_time_total;
56
-
57
- TimeT approximate_processor_usage() const {
58
- return processor_time_total / wall_time_total;
59
- }
60
- };
61
-
62
- Sample sample() const;
63
- };
64
-
65
- }