makefile 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ dist: xenial
3
+ script: bundle exec rake spec
4
+
5
+ matrix:
6
+ include:
7
+ - os: linux
8
+ rvm: 2.7.2
9
+ - os: osx
10
+ rvm: 2.7.2
11
+ fast_finish: true
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in makefile.gemspec
4
+ gemspec
@@ -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.
@@ -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
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.rspec_opts = "-fd"
6
+ end
7
+ task :default => :spec
@@ -0,0 +1,7 @@
1
+ # Defines a namespace for makefile parser
2
+ module Makefile
3
+ require "makefile/version"
4
+ require "makefile/macro"
5
+ require "makefile/suffix_rule"
6
+ require "makefile/reader"
7
+ end
@@ -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,7 @@
1
+ module Makefile
2
+ class Error < ::RuntimeError
3
+ end
4
+
5
+ class ParseError < Error
6
+ end
7
+ 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
@@ -0,0 +1,3 @@
1
+ module Makefile
2
+ VERSION = "0.0.1.pre"
3
+ end
@@ -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
@@ -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
@@ -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