makefile 0.0.1.pre
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +37 -0
- data/Rakefile +7 -0
- data/lib/makefile.rb +7 -0
- data/lib/makefile/command.rb +21 -0
- data/lib/makefile/errors.rb +7 -0
- data/lib/makefile/expression.rb +61 -0
- data/lib/makefile/macro.rb +45 -0
- data/lib/makefile/reader.rb +59 -0
- data/lib/makefile/suffix_rule.rb +15 -0
- data/lib/makefile/target.rb +29 -0
- data/lib/makefile/version.rb +3 -0
- data/makefile.gemspec +25 -0
- data/spec/command_spec.rb +48 -0
- data/spec/expression_spec.rb +171 -0
- data/spec/reader_spec.rb +126 -0
- data/spec/spec_helper.rb +1 -0
- metadata +123 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 91375163dc2731c3a0e2d1ca9e2f8470e9881bb9d111b9923a8b462708d7c029
|
4
|
+
data.tar.gz: 7ed3732d26e82c8f19b1614417105456089d0ca413461d77e252fadd5efad9b1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 34c99ce75a8561f0223febaa24aaecba36578ca65ddf0103ae6a1471e5df38a8b38e3742970ad779e0e517fd55dfd4a32b4a0d573d928344d378c4bcd8c9521f
|
7
|
+
data.tar.gz: f48064e328200049c8491e468f01d55d607d1df6e6b3d3e3f0820dfee174d67885b912e14425b70c86e76a9c199ed1cf68e0683b654452608cf238f32de0922f
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Yuki Sonoda (Yugui)
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Makefile
|
2
|
+
|
3
|
+
An incomplete implementation of Makefile parser.
|
4
|
+
|
5
|
+
It supports a subset of GNU Make's syntax.
|
6
|
+
|
7
|
+
## Project Status
|
8
|
+
|
9
|
+
Pre-alpha. APIs can change without notice or backward compatibility.
|
10
|
+
|
11
|
+
The current priority is to parse CRuby's makefiles.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'makefile'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install makefile
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
TODO: Write usage instructions here
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
1. Fork it
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/makefile.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module Makefile
|
4
|
+
class Command
|
5
|
+
def initialize(raw_cmd)
|
6
|
+
@raw_cmd = raw_cmd
|
7
|
+
@cmd = Expression.new(raw_cmd)
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :raw_cmd, :cmd
|
11
|
+
|
12
|
+
def ==(rhs)
|
13
|
+
self.raw_cmd == rhs.raw_cmd
|
14
|
+
end
|
15
|
+
|
16
|
+
def argv(target, macroset)
|
17
|
+
args = cmd.evaluate(target, macroset).sub(/\A@/, '')
|
18
|
+
Shellwords.split(args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Makefile
|
4
|
+
MACRO_REF_PATTERN = %r!
|
5
|
+
\$ (?:
|
6
|
+
\(
|
7
|
+
(?<paren> [^:)]+ ) (?: :(?<paren_subst>[^=]+) = (?<paren_substval>[^)]*) )?
|
8
|
+
\) |
|
9
|
+
{
|
10
|
+
(?<brace> [^:}]+ ) (?: :(?<brace_subst>[^=]+) = (?<brace_substval>[^}]*) )?
|
11
|
+
} |
|
12
|
+
(?<single> [^({] )
|
13
|
+
)
|
14
|
+
!x
|
15
|
+
|
16
|
+
# An expression which can contain macro reference
|
17
|
+
class Expression
|
18
|
+
def initialize(raw_text)
|
19
|
+
@raw_text = raw_text
|
20
|
+
end
|
21
|
+
attr_reader :raw_text
|
22
|
+
|
23
|
+
def evaluate(target=nil, macroset)
|
24
|
+
evaluate_internal(target, macroset, Set.new)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Shows some implementation details of #evaluate
|
28
|
+
#
|
29
|
+
# Only Makefile::Macro is allowed to call this method.
|
30
|
+
# Others should use #evaluate
|
31
|
+
def evaluate_internal(target, macroset, parent_refs)
|
32
|
+
raw_text.gsub(MACRO_REF_PATTERN) do
|
33
|
+
match = $~
|
34
|
+
case
|
35
|
+
when match[:single]
|
36
|
+
type, name = :single, $~[:single]
|
37
|
+
when match[:paren]
|
38
|
+
type = :quoted
|
39
|
+
name = match[:paren]
|
40
|
+
substpat, substexpr = match[:paren_subst], match[:paren_substval]
|
41
|
+
when match[:brace]
|
42
|
+
type = :quoted
|
43
|
+
name = match[:brace]
|
44
|
+
substpat, substexpr = match[:brace_subst], match[:brace_substval]
|
45
|
+
else
|
46
|
+
raise 'never reach'
|
47
|
+
end
|
48
|
+
|
49
|
+
macro = macroset[name]
|
50
|
+
if macro&.match?(type)
|
51
|
+
expanded = macro.expand_internal(target, macroset, parent_refs)
|
52
|
+
next expanded unless substpat
|
53
|
+
|
54
|
+
replacement = Expression.new(substexpr).
|
55
|
+
evaluate_internal(target, macroset, parent_refs)
|
56
|
+
expanded.gsub(/#{Regexp.escape substpat}(?=\s|$)/, replacement)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'makefile/expression'
|
2
|
+
|
3
|
+
module Makefile
|
4
|
+
class Macro
|
5
|
+
def initialize(name, raw_value = nil, allow_single: true, allow_quoted: true, &block)
|
6
|
+
raise ArgumentError, 'either raw_value or block must be given' unless \
|
7
|
+
raw_value or block
|
8
|
+
|
9
|
+
@name = name
|
10
|
+
@raw_value = raw_value
|
11
|
+
@value = Expression.new(raw_value) if raw_value
|
12
|
+
@allow_single = allow_single
|
13
|
+
@allow_quoted = allow_quoted
|
14
|
+
@block = block
|
15
|
+
end
|
16
|
+
attr_reader :name, :raw_value, :value
|
17
|
+
|
18
|
+
def match?(type)
|
19
|
+
case type
|
20
|
+
when :single
|
21
|
+
return @allow_single
|
22
|
+
when :quoted
|
23
|
+
return @allow_quoted
|
24
|
+
else
|
25
|
+
raise ArgumentError, 'must be :single or :quoted'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Shows some implementation details of #expand
|
30
|
+
#
|
31
|
+
# Only Makefile::Expression is allowed to call this method.
|
32
|
+
def expand_internal(target, macroset, parent_refs)
|
33
|
+
raise Makefile::Error, "Macro #{name} references itself" \
|
34
|
+
if parent_refs.include?(name)
|
35
|
+
|
36
|
+
parent_refs << name
|
37
|
+
begin
|
38
|
+
expr = value || Expression.new(@block.call(target, macroset))
|
39
|
+
expr.evaluate_internal(target, macroset, parent_refs)
|
40
|
+
ensure
|
41
|
+
parent_refs.delete name
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'makefile/command'
|
2
|
+
require 'makefile/target'
|
3
|
+
require 'makefile/errors'
|
4
|
+
|
5
|
+
class Makefile::Reader
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize(input)
|
9
|
+
@input = input
|
10
|
+
end
|
11
|
+
|
12
|
+
def each
|
13
|
+
rule = nil
|
14
|
+
while line = read_line
|
15
|
+
next if line.chomp.empty?
|
16
|
+
if line.start_with?("\t")
|
17
|
+
raise Makefile::ParseError, "commands outside of rule at line #{lineno}" unless rule
|
18
|
+
command = Makefile::Command.new(line[1..-1])
|
19
|
+
rule.add_command(command)
|
20
|
+
next
|
21
|
+
else
|
22
|
+
yield rule if rule
|
23
|
+
rule = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
case line
|
27
|
+
when /\A([[:alpha:]_.][[:alnum:]_.-]*)\s*=\s*(.*)$/
|
28
|
+
yield Makefile::Macro.new($1, $2)
|
29
|
+
when /^(\.[^.]+)(\.[^.]+)?:$/
|
30
|
+
rule = Makefile::SuffixRule.new($1, $2)
|
31
|
+
when /^(.+):(.*)$/
|
32
|
+
rule = Makefile::Target.new($1, raw_deps: $2.strip)
|
33
|
+
else
|
34
|
+
raise NotImplementedError, "Unrecognized line #{line.dump} at #{lineno}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
yield rule if rule
|
38
|
+
end
|
39
|
+
|
40
|
+
def read
|
41
|
+
to_a
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def read_line
|
46
|
+
return nil if @input.eof?
|
47
|
+
line = ""
|
48
|
+
begin
|
49
|
+
fragment = @input.readline
|
50
|
+
fragment = fragment.sub(/#.*$/, '')
|
51
|
+
line << fragment
|
52
|
+
end while !@input.eof? and line.sub!(/\\\r?\n?/, ' ')
|
53
|
+
return line
|
54
|
+
end
|
55
|
+
|
56
|
+
def lineno
|
57
|
+
@input.lineno
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Makefile; end
|
2
|
+
|
3
|
+
class Makefile::SuffixRule
|
4
|
+
def initialize(source, target)
|
5
|
+
@source = source
|
6
|
+
@target = target
|
7
|
+
@commands = []
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :source, :target, :commands
|
11
|
+
|
12
|
+
def add_command(command)
|
13
|
+
@commands << command
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Makefile
|
2
|
+
class Target
|
3
|
+
def initialize(name, raw_deps: nil, commands: [])
|
4
|
+
@name = name
|
5
|
+
if raw_deps
|
6
|
+
@raw_deps = [raw_deps]
|
7
|
+
@deps = [Expression.new(raw_deps)]
|
8
|
+
else
|
9
|
+
@raw_deps, @deps = [], []
|
10
|
+
end
|
11
|
+
@commands = commands
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :name, :raw_deps, :commands
|
15
|
+
|
16
|
+
def deps(macroset)
|
17
|
+
@deps.map {|expr| expr.evaluate(macroset).split(/\s+/) }.flatten
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_dependency(raw_deps)
|
21
|
+
@raw_deps << raw_deps
|
22
|
+
@deps << Expression.new(raw_deps)
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_command(command)
|
26
|
+
@commands << command
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/makefile.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'makefile/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "makefile"
|
8
|
+
spec.version = Makefile::VERSION
|
9
|
+
spec.authors = ["Yuki Yugui Sonoda"]
|
10
|
+
spec.email = ["yugui@yugui.jp"]
|
11
|
+
spec.description = %q{Makefile parser}
|
12
|
+
spec.summary = %q{Makefile parser}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 2.1"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "rr"
|
25
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
require 'makefile'
|
3
|
+
|
4
|
+
describe Makefile::Command do
|
5
|
+
describe '#argv' do
|
6
|
+
it "spilts shell tokens" do
|
7
|
+
cmd = Makefile::Command.new("ls -l spec/command_spec.rb")
|
8
|
+
target = double('target')
|
9
|
+
argv = cmd.argv(target, {})
|
10
|
+
|
11
|
+
expect(argv).to eq(%w[ ls -l spec/command_spec.rb ])
|
12
|
+
end
|
13
|
+
|
14
|
+
it "evaluates macro before split " do
|
15
|
+
cmd = Makefile::Command.new("${A}$(B) C$(D)")
|
16
|
+
target = double('target')
|
17
|
+
argv = cmd.argv(
|
18
|
+
target,
|
19
|
+
'A' => Makefile::Macro.new('A', 'a '),
|
20
|
+
'B' => Makefile::Macro.new('B', 'b '),
|
21
|
+
'D' => Makefile::Macro.new('D', 'd '),
|
22
|
+
)
|
23
|
+
|
24
|
+
expect(argv).to eq(%w[ a b Cd ])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "propagates target to macro expansion" do
|
28
|
+
cmd = Makefile::Command.new("${A}")
|
29
|
+
target = double('target')
|
30
|
+
expect(cmd.cmd).to receive(:evaluate).with(target, anything).and_call_original
|
31
|
+
|
32
|
+
cmd.argv(
|
33
|
+
target,
|
34
|
+
'A' => Makefile::Macro.new('A') { 'a' },
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "ignores silence marker" do
|
39
|
+
cmd = Makefile::Command.new("$(Q)echo 1")
|
40
|
+
argv = cmd.argv(
|
41
|
+
double('target'),
|
42
|
+
'Q' => Makefile::Macro.new('Q', '@'),
|
43
|
+
)
|
44
|
+
|
45
|
+
expect(argv).to eq(%w[ echo 1 ])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
require 'makefile'
|
3
|
+
|
4
|
+
describe Makefile::Expression do
|
5
|
+
describe '#evaluate' do
|
6
|
+
it 'returns literal expression as is' do
|
7
|
+
expr = Makefile::Expression.new('abcde fghij')
|
8
|
+
target = double('target')
|
9
|
+
macroset = double('macroset')
|
10
|
+
|
11
|
+
result = expr.evaluate(target, macroset)
|
12
|
+
|
13
|
+
expect(result).to eq('abcde fghij')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'expands single letter macros' do
|
17
|
+
expr = Makefile::Expression.new('A$BCDE $F $G')
|
18
|
+
result = expr.evaluate(
|
19
|
+
double('target'),
|
20
|
+
'B' => Makefile::Macro.new('B', 'b'),
|
21
|
+
'F' => Makefile::Macro.new('F', 'f'),
|
22
|
+
'G' => Makefile::Macro.new('G', 'g'),
|
23
|
+
)
|
24
|
+
|
25
|
+
expect(result).to eq('AbCDE f g')
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'expands parenthesized macros' do
|
29
|
+
expr = Makefile::Expression.new('A$(BCD)E $(FG)')
|
30
|
+
result = expr.evaluate(
|
31
|
+
double('target'),
|
32
|
+
'B' => Makefile::Macro.new('B', 'b'),
|
33
|
+
'BCD' => Makefile::Macro.new('BCD', '123'),
|
34
|
+
'FG' => Makefile::Macro.new('FG', '45'),
|
35
|
+
)
|
36
|
+
|
37
|
+
expect(result).to eq('A123E 45')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'expands braced macros' do
|
41
|
+
expr = Makefile::Expression.new('A${BCD}E ${FG}')
|
42
|
+
result = expr.evaluate(
|
43
|
+
double('target'),
|
44
|
+
'B' => Makefile::Macro.new('B', 'b'),
|
45
|
+
'BCD' => Makefile::Macro.new('BCD', '123'),
|
46
|
+
'FG' => Makefile::Macro.new('FG', '45'),
|
47
|
+
)
|
48
|
+
|
49
|
+
expect(result).to eq('A123E 45')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'expands undefined macros into blank' do
|
53
|
+
expr = Makefile::Expression.new('A$BCD$(EF)G${HI}')
|
54
|
+
result = expr.evaluate(double('target'), {})
|
55
|
+
|
56
|
+
expect(result).to eq('ACDG')
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'raises an exception on self-recursion' do
|
60
|
+
expr = Makefile::Expression.new('$A')
|
61
|
+
expect {
|
62
|
+
expr.evaluate(
|
63
|
+
double('target'),
|
64
|
+
'A' => Makefile::Macro.new('A', '_${A}'),
|
65
|
+
)
|
66
|
+
}.to raise_error(Makefile::Error)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'raises an exception on mutual recursion' do
|
70
|
+
expr = Makefile::Expression.new('$A')
|
71
|
+
expect {
|
72
|
+
expr.evaluate(
|
73
|
+
double('target'),
|
74
|
+
'A' => Makefile::Macro.new('A', '_${B}'),
|
75
|
+
'B' => Makefile::Macro.new('B', '-${A}'),
|
76
|
+
)
|
77
|
+
}.to raise_error(Makefile::Error)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'expands $$ to $' do
|
81
|
+
expr = Makefile::Expression.new('$${A}')
|
82
|
+
result = expr.evaluate(
|
83
|
+
double('target'),
|
84
|
+
'A' => 'a',
|
85
|
+
'$' => Makefile::Macro.new('$', '$', allow_quoted: false),
|
86
|
+
)
|
87
|
+
|
88
|
+
expect(result).to eq('${A}')
|
89
|
+
end
|
90
|
+
|
91
|
+
%w[ $($) ${$} ].each do |ref|
|
92
|
+
it "does not expand #{ref} with the default rule" do
|
93
|
+
expr = Makefile::Expression.new(ref)
|
94
|
+
result = expr.evaluate(
|
95
|
+
double('target'),
|
96
|
+
'$' => Makefile::Macro.new('$', '$', allow_quoted: false),
|
97
|
+
)
|
98
|
+
|
99
|
+
expect(result).to eq("")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'expands only once' do
|
104
|
+
expr = Makefile::Expression.new('$T')
|
105
|
+
result = expr.evaluate(
|
106
|
+
double('target'),
|
107
|
+
'M' => Makefile::Macro.new('M', '$$'),
|
108
|
+
'N' => Makefile::Macro.new('N', '(S)'),
|
109
|
+
'S' => Makefile::Macro.new('S', '1'),
|
110
|
+
'T' => Makefile::Macro.new('T', '$(M)$(N)'),
|
111
|
+
'$' => Makefile::Macro.new('$', '$', allow_quoted: false),
|
112
|
+
)
|
113
|
+
|
114
|
+
expect(result).to eq('$(S)')
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'allows hook variables' do
|
118
|
+
expr = Makefile::Expression.new('$A')
|
119
|
+
target = double('target')
|
120
|
+
called = false
|
121
|
+
macroset = {
|
122
|
+
'A' => Makefile::Macro.new('A') do |t, m|
|
123
|
+
called = true
|
124
|
+
expect(t).to equal(target)
|
125
|
+
expect(m).to eq(macroset)
|
126
|
+
|
127
|
+
'abcde'
|
128
|
+
end
|
129
|
+
}
|
130
|
+
result = expr.evaluate(target, macroset)
|
131
|
+
|
132
|
+
expect(called).to be_truthy
|
133
|
+
expect(result).to eq('abcde')
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'substitutes on macro expansion' do
|
137
|
+
expr = Makefile::Expression.new('$A')
|
138
|
+
result = expr.evaluate(
|
139
|
+
double('target'),
|
140
|
+
'A' => Makefile::Macro.new('A', '$(B:.S=.o)'),
|
141
|
+
'B' => Makefile::Macro.new('B', '$(C:.c=.S)'),
|
142
|
+
'C' => Makefile::Macro.new('C', "foo.c bar.c\tbaz.c\vqux.c"),
|
143
|
+
)
|
144
|
+
|
145
|
+
expect(result).to eq("foo.o bar.o\tbaz.o\vqux.o")
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'evaluates replacement text on expansion' do
|
149
|
+
expr = Makefile::Expression.new('$A')
|
150
|
+
result = expr.evaluate(
|
151
|
+
double('target'),
|
152
|
+
'A' => Makefile::Macro.new('A', '$(B:.c=$S)'),
|
153
|
+
'B' => Makefile::Macro.new('B', "foo.c bar.c\tbaz.c\vqux.c"),
|
154
|
+
'S' => Makefile::Macro.new('S', '${T}'),
|
155
|
+
'T' => Makefile::Macro.new('T', '.o'),
|
156
|
+
)
|
157
|
+
|
158
|
+
expect(result).to eq("foo.o bar.o\tbaz.o\vqux.o")
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'replace patterns only before whitespace' do
|
162
|
+
expr = Makefile::Expression.new('$(A:abc=123)')
|
163
|
+
result = expr.evaluate(
|
164
|
+
double('target'),
|
165
|
+
'$' => Makefile::Macro.new('$', '$', allow_quoted: false),
|
166
|
+
'A' => Makefile::Macro.new('A', "abc.abc abc-abc\tabc$$abc\vabc")
|
167
|
+
)
|
168
|
+
expect(result).to eq("abc.123 abc-123\tabc$123\v123")
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
data/spec/reader_spec.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
require 'makefile'
|
3
|
+
|
4
|
+
describe Makefile::Reader do
|
5
|
+
def create_input_stub(lines)
|
6
|
+
lines = lines.dup
|
7
|
+
input = double('input')
|
8
|
+
allow(input).to receive(:eof?) { lines.empty? }
|
9
|
+
allow(input).to receive(:readline) { lines.shift }
|
10
|
+
input
|
11
|
+
end
|
12
|
+
|
13
|
+
it "reads input with #read_line" do
|
14
|
+
eof = false
|
15
|
+
input = double('input')
|
16
|
+
allow(input).to receive(:eof?) { eof }
|
17
|
+
expect(input).to receive(:readline) { "a=b\n" }
|
18
|
+
expect(input).to receive(:readline) { eof = true; "b=c" }
|
19
|
+
|
20
|
+
Makefile::Reader.new(input).read
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#each" do
|
24
|
+
it "returns a Macro object on read a macro" do
|
25
|
+
input = create_input_stub(<<-EOF.lines)
|
26
|
+
MACRO1=1
|
27
|
+
EOF
|
28
|
+
|
29
|
+
macro, = *Makefile::Reader.new(input).read
|
30
|
+
expect(macro).to be_an_instance_of(Makefile::Macro)
|
31
|
+
expect(macro.name).to eq("MACRO1")
|
32
|
+
expect(macro.raw_value).to eq("1")
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'ignores whitespaces around macro assignment' do
|
36
|
+
input = create_input_stub(<<-EOF.lines)
|
37
|
+
MACRO1 =1
|
38
|
+
MACRO2= 2
|
39
|
+
MACRO3 = 3
|
40
|
+
MACRO4 \t\v=\t\v 4
|
41
|
+
EOF
|
42
|
+
|
43
|
+
macros = Makefile::Reader.new(input).read
|
44
|
+
expect(macros).to all(be_an_instance_of(Makefile::Macro))
|
45
|
+
expect(macros.size).to be(4)
|
46
|
+
|
47
|
+
macros.each.with_index do |macro, i|
|
48
|
+
expect(macro.name).to eq("MACRO#{i+1}")
|
49
|
+
expect(macro.raw_value).to eq("#{i+1}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "reads multiline macro" do
|
54
|
+
input = create_input_stub(<<-EOF.lines)
|
55
|
+
MACRO1=1 \\
|
56
|
+
\t2 \\
|
57
|
+
\v3 \\
|
58
|
+
4
|
59
|
+
EOF
|
60
|
+
|
61
|
+
macro, = *Makefile::Reader.new(input).read
|
62
|
+
expect(macro).to be_an_instance_of(Makefile::Macro)
|
63
|
+
expect(macro.name).to eq("MACRO1")
|
64
|
+
expect(macro.raw_value).to eq("1 \t2 \v3 4")
|
65
|
+
end
|
66
|
+
|
67
|
+
it "returns a SuffixRule object on read a rule definition" do
|
68
|
+
input = create_input_stub(<<-EOF.lines)
|
69
|
+
.c.o:
|
70
|
+
$(CC) -c -o $@ $<
|
71
|
+
EOF
|
72
|
+
|
73
|
+
rule, = *Makefile::Reader.new(input).read
|
74
|
+
expect(rule).to be_an_instance_of(Makefile::SuffixRule)
|
75
|
+
expect(rule.source).to eq(".c")
|
76
|
+
expect(rule.target).to eq(".o")
|
77
|
+
expect(rule.commands.size).to eq(1)
|
78
|
+
expect(rule.commands[0]).to eq(
|
79
|
+
Makefile::Command.new("$(CC) -c -o $@ $<\n"))
|
80
|
+
end
|
81
|
+
|
82
|
+
it "reads multiline command" do
|
83
|
+
input = create_input_stub(<<-EOF.lines)
|
84
|
+
.c.o:
|
85
|
+
\t$(ECHO) \\
|
86
|
+
1 \\
|
87
|
+
\t2 \\
|
88
|
+
\v3
|
89
|
+
EOF
|
90
|
+
|
91
|
+
rule, = *Makefile::Reader.new(input).read
|
92
|
+
expect(rule.commands.size).to eq(1)
|
93
|
+
expect(rule.commands[0]).to eq(
|
94
|
+
Makefile::Command.new("$(ECHO) 1 \t2 \v3\n"))
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'returns a Target object on reading a target definition' do
|
98
|
+
input = create_input_stub(<<-EOF.lines)
|
99
|
+
foo: bar$(EXT) baz
|
100
|
+
$(CC) -c -o foo bar baz
|
101
|
+
EOF
|
102
|
+
|
103
|
+
target, = *Makefile::Reader.new(input).read
|
104
|
+
expect(target).to be_an_instance_of(Makefile::Target)
|
105
|
+
expect(target.name).to eq('foo')
|
106
|
+
expect(target.raw_deps).to eq(['bar$(EXT) baz'])
|
107
|
+
expect(target.commands.size).to eq(1)
|
108
|
+
expect(target.commands[0]).to eq(
|
109
|
+
Makefile::Command.new("$(CC) -c -o foo bar baz\n"))
|
110
|
+
end
|
111
|
+
|
112
|
+
it "skips comments" do
|
113
|
+
input = create_input_stub(<<-EOF.lines)
|
114
|
+
MACRO1=1# test
|
115
|
+
# MACRO2=2
|
116
|
+
EOF
|
117
|
+
|
118
|
+
iter = Makefile::Reader.new(input).enum_for(:each)
|
119
|
+
macro = iter.next
|
120
|
+
expect(macro).to be_an_instance_of(Makefile::Macro)
|
121
|
+
expect(macro.name).to eq("MACRO1")
|
122
|
+
expect(macro.raw_value).to eq("1")
|
123
|
+
expect { iter.next }.to raise_error(StopIteration)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rspec'
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: makefile
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.pre
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yuki Yugui Sonoda
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-11-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.1'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rr
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Makefile parser
|
70
|
+
email:
|
71
|
+
- yugui@yugui.jp
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".travis.yml"
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- lib/makefile.rb
|
83
|
+
- lib/makefile/command.rb
|
84
|
+
- lib/makefile/errors.rb
|
85
|
+
- lib/makefile/expression.rb
|
86
|
+
- lib/makefile/macro.rb
|
87
|
+
- lib/makefile/reader.rb
|
88
|
+
- lib/makefile/suffix_rule.rb
|
89
|
+
- lib/makefile/target.rb
|
90
|
+
- lib/makefile/version.rb
|
91
|
+
- makefile.gemspec
|
92
|
+
- spec/command_spec.rb
|
93
|
+
- spec/expression_spec.rb
|
94
|
+
- spec/reader_spec.rb
|
95
|
+
- spec/spec_helper.rb
|
96
|
+
homepage: ''
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata: {}
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">"
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: 1.3.1
|
114
|
+
requirements: []
|
115
|
+
rubygems_version: 3.1.2
|
116
|
+
signing_key:
|
117
|
+
specification_version: 4
|
118
|
+
summary: Makefile parser
|
119
|
+
test_files:
|
120
|
+
- spec/command_spec.rb
|
121
|
+
- spec/expression_spec.rb
|
122
|
+
- spec/reader_spec.rb
|
123
|
+
- spec/spec_helper.rb
|