litbuild 1.0.14 → 1.0.16

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f420862b3a3dd758ef05aed2b717defe89a48a34944b25b3af4042a0d47a749
4
- data.tar.gz: bfb4c6364c57b9b9220d0bd43113f4a8b2ff0639b106b675a9e8af3c85982eab
3
+ metadata.gz: c8e6cf1338409a6f92412c572ddca22dfea69dec035c023ec029aa73b56f833f
4
+ data.tar.gz: 9fe0eb1366a7404cfe258081c3ef4f3f96e5214cba7f6d428dceac520d35da0f
5
5
  SHA512:
6
- metadata.gz: 22838c4497fdb70a5140507dcc0a10f511f9751bd36197ec3dc88a8c5e7bc6fda27b8678bd34f46ea7d21b9f06c944e52b3bcd7ffee00713d9aa34a56d168387
7
- data.tar.gz: 01f7b1eab7fe96e96fbd04dd446743259786bb54a8556f379627c13153c0e917ea6af368fdc4bb3c4f201666f32d78380a9c416ee238e3e39a4c07305b200edd
6
+ metadata.gz: 6f79a4716874345084ec1ac7fe7c41049cd09db46d46e7349125767a9598b7a079314dac3630fcc221c0365a0a6bdb033bede9a1c41ce772b5740e01c15b3a53
7
+ data.tar.gz: e4db23446ce1334ec23ad2e1b566195ca0eb3515cbf7876089498f144af5e9e92302b87ec8ad9c6495c61935d4dfc90b5803acb979b603cddbbefcc8890e4054
@@ -285,14 +285,14 @@ module Litbuild
285
285
  def write_parameter(doc, params)
286
286
  params.each do |param|
287
287
  pname = param['name'].first
288
- pdefault = param['default'].first.gsub(/\\\n /, '')
288
+ pdefault = param['default'].first.gsub(/[\\\s]+/m, ' ')
289
289
  default_string = if pdefault == '(empty)'
290
290
  'not set'
291
291
  else
292
292
  "`#{pdefault}`"
293
293
  end
294
294
 
295
- value = @parameters[pname].gsub(/\\\n /, '')
295
+ value = @parameters[pname].gsub(/[\\\s]+/m, ' ')
296
296
  val_string = if value == ''
297
297
  'not set'
298
298
  else
@@ -510,9 +510,7 @@ module Litbuild
510
510
  def render_service_pipeline(script, spipe)
511
511
  pname = spipe['name'].first
512
512
  spipe['bundle']&.each do |bundle|
513
- script.puts("mkdir -p #{bundle}")
514
- script.puts("grep -q '^#{pname}$' #{bundle}/contents || " \
515
- "echo #{pname} >> #{bundle}/contents")
513
+ render_add_to_bundle(script, bundle, pname)
516
514
  end
517
515
  sdirs = spipe['servicedirs']
518
516
  sdirs.each_with_index do |sdir, i|
@@ -533,17 +531,12 @@ module Litbuild
533
531
 
534
532
  def render_service_dir(script, sdir)
535
533
  sd = ServiceDir.new(sdir)
536
- if sd.bundle
537
- script.puts("grep -q '^#{sd.name}$' #{sd.bundle}/contents || " \
538
- "echo #{sd.name} >> #{sd.bundle}/contents")
539
- end
534
+ render_add_to_bundle(script, sd.bundle, sd.name) if sd.bundle
540
535
  script.puts("mkdir -p #{sd.name}")
541
536
  sd.oneline_files.keys.sort.each do |fn|
542
537
  script.puts("echo #{sd.oneline_files[fn]} > #{sd.name}/#{fn}")
543
538
  end
544
539
  multiline = sd.multiline_files
545
- deps = sd.dependencies
546
- multiline['dependencies'] = [deps.join("\n")] unless deps.empty?
547
540
  multiline.keys.sort.each do |filename|
548
541
  # I always terminate here documents in litbuild-generated
549
542
  # scripts with "LBEOF", partly because I'm thinking "Litbuild
@@ -561,9 +554,22 @@ module Litbuild
561
554
  script.puts("echo '#{env[envvar]}' > #{sd.name}/env/#{envvar}")
562
555
  end
563
556
  end
557
+ unless sd.dependencies.empty?
558
+ script.puts("mkdir -p #{sd.name}/dependencies.d")
559
+ sd.dependencies.each do |dep|
560
+ script.puts("touch #{sd.name}/dependencies.d/#{dep}")
561
+ end
562
+ end
564
563
  skip_line(script)
565
564
  end
566
565
 
566
+ def render_add_to_bundle(script, bundle, svc)
567
+ script.puts("mkdir -p #{bundle}")
568
+ script.puts("echo bundle > #{bundle}/type")
569
+ script.puts("mkdir -p #{bundle}/contents.d")
570
+ script.puts("touch #{bundle}/contents.d/#{svc}")
571
+ end
572
+
567
573
  def render_cfgrepo_trailer(script, blueprint, log)
568
574
  cfgs = blueprint['configuration-files']
569
575
  return unless cfgs
@@ -9,13 +9,15 @@ require 'litbuild/visitor'
9
9
  module Litbuild
10
10
  # Blueprints are described in the `doc` directory.
11
11
  #
12
- # tl;dr: Blueprints are considered as "chunks" separated by blank
13
- # lines. Each chunk can be either directives or narrative. Narrative
14
- # is AsciiDoc and does not get parsed or transformed. Directives are
15
- # basically a simplified version of YAML.
12
+ # tl;dr:
16
13
  #
17
- # After parsing, the narrative is found in the `grafs` variables and
18
- # directives are found in `directive` hashes.
14
+ # Blueprints are considered as "chunks" separated by blank lines.
15
+ # Each chunk can be either directives or narrative.
16
+ # Narrative is AsciiDoc and does not get parsed or transformed.
17
+ # Directives are basically a simplified version of YAML.
18
+ #
19
+ # After parsing, the narrative is found in the `grafs` variables
20
+ # and directives are found in `directive` hashes.
19
21
  class Blueprint
20
22
  class << self
21
23
  # This should simply be the name of the directory where blueprints
@@ -31,45 +33,29 @@ module Litbuild
31
33
  ObjectSpace.each_object(Class).select { |c| c < self }
32
34
  end
33
35
 
34
- # WARNING: in most cases, a freshly-created blueprint cannot be used
35
- # until it has also been _prepared_. (See below.)
36
- def initialize(text:)
37
- parser = BlueprintParser.new(text)
36
+ def initialize(text:, parameters:, logfile_namer:, library:)
37
+ parser = BlueprintParser.new(file_text: text, parameters: parameters)
38
38
  @base_directives = parser.base_directives
39
39
  @phase_directives = parser.phase_directives
40
40
  @base_grafs = parser.base_grafs
41
41
  @phase_grafs = parser.phase_grafs
42
42
  @active_phase = @base_directives['default-phase']&.first
43
- end
44
-
45
- # This prepares a blueprint for use. This is separate from
46
- # initialize because parameters are not available until after all
47
- # blueprints have been parsed, and the logfile namer is not
48
- # available until after the LOGFILE_DIR parameter can be resolved.
49
- #
50
- # Parameters is a set of configuration parameters
51
- # Logfile_namer is used to generate log file names
52
- # Library is the BlueprintLibrary, used (for example) to find
53
- # dependencies.
54
- def prepare(parameters:, logfile_namer:, library:)
55
43
  @logfile_namer = logfile_namer
56
44
  @bp_library = library
57
- [@base_directives,
58
- @phase_directives,
59
- @base_grafs,
60
- @phase_grafs].each { |component| resolve_all(parameters, component) }
61
45
  end
62
46
 
63
- # Dependencies need to be handled before their dependants; that's
64
- # handled here. Subclasses should run `super` before doing anything
65
- # else with the Visitor, or call the send_to_dependencies method
66
- # directly, so that this happens properly!
47
+ # Dependencies need to be rendered before their dependants;
48
+ # that's handled here.
49
+ # Subclasses should run `super` before doing anything else
50
+ # with the Visitor,
51
+ # or call the send_to_dependencies method directly,
52
+ # so that this happens properly!
67
53
  def accept(visitor:)
68
54
  send_to_dependencies(visitor: visitor)
69
55
  end
70
56
 
71
- # Return a copy of this blueprint that is initialized to operate on
72
- # the specified phase.
57
+ # Return a copy of this blueprint
58
+ # that is initialized to operate on the specified phase.
73
59
  def for_phase(new_active_phase)
74
60
  return self unless phases?
75
61
 
@@ -102,10 +88,11 @@ module Litbuild
102
88
  directives[directive]
103
89
  end
104
90
 
105
- # This is just like the [] operator method except that it always
106
- # returns only the first value for a directive -- which is helpful
107
- # for all the directives that are only ever supposed to have a
108
- # single value, like `name` or `full-name`.
91
+ # This is just like the [] operator method except that
92
+ # it always returns only the first value for a directive --
93
+ # which is helpful for all the directives that
94
+ # are only ever supposed to have a single value,
95
+ # like `name` or `full-name`.
109
96
  def value(directive)
110
97
  self[directive]&.first
111
98
  end
@@ -147,24 +134,6 @@ module Litbuild
147
134
  @logfile_namer.path_for(name, phase, stage_name)
148
135
  end
149
136
 
150
- def parameter_defaults
151
- values = {}
152
- all_directive_sets = [@base_directives, @phase_directives.values].flatten
153
- all_directive_sets.each do |dirset|
154
- next unless dirset.key?('parameter')
155
-
156
- dirset['parameter'].each do |a_param|
157
- val = a_param['default'].first
158
- values[a_param['name'].first] = if val == '(empty)'
159
- ''
160
- else
161
- val
162
- end
163
- end
164
- end
165
- values
166
- end
167
-
168
137
  def target_name
169
138
  if active_phase
170
139
  "#{name}::#{active_phase}"
@@ -181,9 +150,11 @@ module Litbuild
181
150
  'FAILURE'
182
151
  end
183
152
 
184
- # If a dependency is declared both without a phase (typically in the
185
- # base directives for the blueprint) and with a phase (typically in
186
- # a phase of that blueprint), throw away the phaseless declaration
153
+ # If a dependency is declared both without a phase
154
+ # (typically in the base directives for the blueprint)
155
+ # and with a phase
156
+ # (typically in # a phase of that blueprint),
157
+ # throw away the phaseless declaration
187
158
  # to avoid including two versions of the dependency.
188
159
  def deduped_dependency_names
189
160
  dep_names = self['depends-on'].clone || []
@@ -210,29 +181,5 @@ module Litbuild
210
181
 
211
182
  @active_phase = phase
212
183
  end
213
-
214
- private
215
-
216
- def resolve_all(parameters, something)
217
- case something
218
- when Hash
219
- something.each_value { |element| resolve_all(parameters, element) }
220
- when Array
221
- something.each { |element| resolve_all(parameters, element) }
222
- when String
223
- resolve(parameters: parameters, a_string: something)
224
- end
225
- end
226
-
227
- def resolve(parameters:, a_string:)
228
- params = a_string.scan(/PARAM\[([A-Z_]+)\]/).flatten.sort.uniq
229
- params.each do |p|
230
- parameters[p] ||
231
- raise(ParameterMissing, "Parameter #{p} is not defined")
232
-
233
- a_string.gsub!(/PARAM\[#{p}\]/, parameters[p])
234
- end
235
- a_string
236
- end
237
184
  end
238
185
  end
@@ -4,12 +4,15 @@ require 'litbuild/commands'
4
4
  require 'litbuild/narrative'
5
5
  require 'litbuild/package'
6
6
  require 'litbuild/section'
7
+ require 'litbuild/blueprint_parser'
7
8
 
8
9
  module Litbuild
9
10
  # BlueprintLibrary initializes and sets configuration parameters for
10
- # blueprints, and provides a common access point for blueprints and
11
- # parameters. It always loads blueprints from the *current working
12
- # directory* and uses config parameters from *the environment*.
11
+ # blueprints,
12
+ # and provides a common access point for blueprints and parameters.
13
+ # It always loads blueprints from the *current working directory*
14
+ # and uses config parameters from *the environment*
15
+ # (or the default values defined in blueprints).
13
16
  class BlueprintLibrary
14
17
  REQUIRED_PARAMS = %w[DOCUMENT_DIR LOGFILE_DIR PATCH_DIR
15
18
  SCRIPT_DIR TARFILE_DIR WORK_SITE].freeze
@@ -17,17 +20,8 @@ module Litbuild
17
20
  attr_reader :blueprints, :parameters
18
21
 
19
22
  def initialize(logfile_namer_class:)
20
- @blueprints = {}
21
- Blueprint.descendants.each do |blueprint_type|
22
- load_blueprints_of_type(blueprint_type)
23
- end
24
23
  @parameters = resolve_parameter_values
25
- log_namer = logfile_namer_class.new(@parameters['LOGFILE_DIR'])
26
- @blueprints.each_value do |bp|
27
- bp.prepare(parameters: @parameters,
28
- logfile_namer: log_namer,
29
- library: self)
30
- end
24
+ @blueprints = load_and_parse_all_blueprints(logfile_namer_class)
31
25
  end
32
26
 
33
27
  def blueprint_for(target:)
@@ -39,8 +33,10 @@ module Litbuild
39
33
  end
40
34
 
41
35
  # Convert the dependency declarations found in a `depends-on`
42
- # directive to actual blueprints. See the `Dependencies` section of
43
- # doc/blueprints.txt for details on how this works.
36
+ # directive to actual blueprints
37
+ # (from the blueprint library).
38
+ # See the `Dependencies` section of doc/blueprints.txt
39
+ # for details on how this is supposed to work.
44
40
  def dependencies_for(blueprint)
45
41
  dep_names = blueprint.deduped_dependency_names
46
42
  dep_names.map do |dep|
@@ -59,8 +55,8 @@ module Litbuild
59
55
 
60
56
  private
61
57
 
62
- # split a target name, like linux::headers, into a blueprint name
63
- # (linux) and a phase name (headers).
58
+ # split a target name, like linux::headers,
59
+ # into a blueprint name (linux) and a phase name (headers).
64
60
  def split_name_and_phase(target:)
65
61
  if target.include?('::')
66
62
  target.strip.match(/(.*)::(.*)/)[1..2]
@@ -69,54 +65,94 @@ module Litbuild
69
65
  end
70
66
  end
71
67
 
72
- # These methods are used during initialization, be cautious when
73
- # modifying them.
68
+ # These methods are used during initialization,
69
+ # be cautious when modifying them.
74
70
 
75
- def load_blueprints_of_type(blueprint_type)
76
- # Sometimes, starting in ruby 2.3, there is an extra singleton
77
- # descendant of Blueprint, or something. (This happens about a
78
- # thrid of the time, so I really mean "soemtime"s.) IDK what's
79
- # going on and I don't feel like fussing with it; this kludge
80
- # works around the issue, whatever it is.
81
- return unless blueprint_type.name
71
+ def blueprint_files_by_type
72
+ fbt = {}
73
+ Blueprint.descendants.each do |bptype|
74
+ fbt[bptype] = Dir.glob("./#{bptype.directory_name}/*.txt")
75
+ end
76
+ fbt
77
+ end
82
78
 
83
- # All blueprints have a name and a full-name. If a blueprint has
84
- # no `name:` or `full-name:` directive, they will be provided at
85
- # load time.
86
- automatic_directives = %w[name full-name]
79
+ def load_and_parse_all_blueprints(logfile_namer_class)
80
+ log_namer = logfile_namer_class.new(@parameters['LOGFILE_DIR'])
87
81
 
88
- Dir.glob("./#{blueprint_type.directory_name}/*.txt").each do |a_file|
89
- bp_text = File.read(a_file)
90
- begin
91
- automatic_directives.each do |directive|
92
- unless bp_text.match?(/^#{directive}:/)
93
- bp_text = "#{directive}: #{File.basename(a_file, '.txt')}\n\n" +
94
- bp_text
82
+ blueprints = {}
83
+ blueprint_files_by_type.each do |bp_type, bp_files|
84
+ bp_files.each do |bp_file|
85
+ bp_text = load_and_add_automatic_directives(bp_file)
86
+ begin
87
+ bp = bp_type.new(text: bp_text, parameters: @parameters,
88
+ logfile_namer: log_namer, library: self)
89
+ if blueprints[bp.name]
90
+ raise(DuplicateBlueprint,
91
+ "Duplicate blueprint #{bp.name} found in #{bp_file}")
95
92
  end
93
+ blueprints[bp.name] = bp
94
+ rescue ArgumentError => e
95
+ raise(Litbuild::ParseError,
96
+ "Could not parse #{bp_file}}: #{e.message}")
96
97
  end
97
- bp = blueprint_type.new(text: bp_text)
98
- rescue ArgumentError => e
99
- raise(Litbuild::ParseError, "Could not parse #{a_file}: #{e.message}")
100
- end
101
- if @blueprints[bp.name]
102
- raise(DuplicateBlueprint,
103
- "Duplicate blueprint #{bp.name} found in #{a_file}")
104
- else
105
- @blueprints[bp.name] = bp
106
98
  end
107
99
  end
100
+ blueprints
101
+ end
102
+
103
+ # All blueprints have a name and a full-name.
104
+ # If a blueprint has no `name:` or `full-name:` directive,
105
+ # they will be provided at load time
106
+ # (the basename of the blueprint file is used for either
107
+ # or both missing directives)
108
+ def load_and_add_automatic_directives(filename)
109
+ text = File.read(filename)
110
+ name = File.basename(filename, '.txt')
111
+ automatic_directives = %w[name full-name]
112
+ automatic_directives.each do |dir|
113
+ text = "#{dir}: #{name}\n\n" + text unless text.match?(/^#{dir}:/)
114
+ end
115
+ text
116
+ end
117
+
118
+ # This does an initial parse of blueprints that declare parameters,
119
+ # and returns just the result of parsing those declarations.
120
+ def parameters_from(blueprint_text)
121
+ lines = blueprint_text.lines
122
+
123
+ return {} unless lines.detect { |l| l.start_with?('parameter:') }
124
+
125
+ # For purposes of finding parameters, phases, parameter
126
+ # references for substitution, and conditional blocks are
127
+ # irrelevant.
128
+ simplified = lines.grep_v(/^phase:/).grep_v(/^#/).join
129
+ simplified.gsub!(/PARAM\[[A-Z_]+\]/, 'UNSET')
130
+
131
+ parser = BlueprintParser.new(file_text: simplified)
132
+ param_directives = parser.base_directives['parameter']
133
+ params = {}
134
+ param_directives.each do |a_param|
135
+ default_val = a_param['default'].first
136
+ params[a_param['name'].first] = if default_val == '(empty)'
137
+ ''
138
+ else
139
+ default_val.gsub(/[\\\s]+/m, ' ')
140
+ end
141
+ end
142
+ params
108
143
  end
109
144
 
110
145
  def resolve_parameter_values
111
- values = {}
112
- REQUIRED_PARAMS.each { |key| values[key] = 'UNSET' }
113
- @blueprints.each_value do |bp|
114
- values.merge!(bp.parameter_defaults)
146
+ params = {}
147
+ REQUIRED_PARAMS.each { |key| params[key] = 'UNSET' }
148
+ bp_files = blueprint_files_by_type.values.flatten
149
+ bp_files.each do |bp_file|
150
+ params.merge!(parameters_from(File.read(bp_file)))
115
151
  end
116
152
  ENV.each do |k, v|
117
- values[k] = v if values.key?(k)
153
+ params[k] = v if params.key?(k)
118
154
  end
119
- @parameters = values
155
+ @parameters = params
120
156
  end
121
157
  end
122
158
  end
@@ -5,36 +5,53 @@ require 'litbuild/string_indentation'
5
5
  require 'json'
6
6
 
7
7
  module Litbuild
8
- # This is a kludgy hand-built parser. Blueprint structure is not (at
9
- # least, at this point) complicated enough that I can see any point in
10
- # defining a grammar and using a parser generator. The structure of
11
- # blueprints is described informally in `doc/blueprints.txt` (and
12
- # blueprint-type-specific files under `doc` as well).
8
+ # This is a kludgy hand-built parser,
9
+ # written by someone who does not know how to write parsers.
10
+ # Blueprint structure is not
11
+ # (at least, at this point)
12
+ # complicated enough that I can see any point in defining a grammar
13
+ # and using a parser generator.
14
+ # The structure of blueprints,
15
+ # and the intended behavior of this parser,
16
+ # are described informally in
17
+ # `doc/blueprints.txt`
18
+ # (and blueprint-type-specific files under `doc` as well).
19
+ #
20
+ # The entire parse process occurs during initialization,
21
+ # so after creating a BlueprintParser you will either be able
22
+ # to ask it for the result of the parse operation,
23
+ # or have an exception to deal with.
24
+ #
25
+ # Essentially all methods are therefore used within initialization.
26
+ # Use great care when modifying this class!
13
27
  class BlueprintParser
14
28
  include StringIndentation
15
29
 
16
- # _directives_ are for use in scripts. _grafs_ are for use in
17
- # documents.
30
+ # _directives_ are for use in scripts.
31
+ # _grafs_ are for use in documents.
18
32
  attr_reader :base_directives, :phase_directives, :base_grafs, :phase_grafs
19
33
 
20
- def initialize(file_text)
21
- @text = file_text
34
+ def initialize(file_text:, parameters: nil)
35
+ @text = file_text.dup
36
+ substitute_parameters(parameters) if parameters
37
+ resolve_conditional_blocks if @text.match?(/^#IF /)
38
+
22
39
  @phase_directives = {}
23
40
  @phase_grafs = {}
24
41
  phase_chunks = @text.split(/^phase: ([a-z -_]+)$/)
25
42
 
26
- # The first part of the blueprint, before any phase directive,
27
- # becomes the base directives and base narrative for the
28
- # blueprint. If there are no phase directives, this is the entire
29
- # blueprint, obvs.
43
+ # The first part of the blueprint,
44
+ # before any phase directive,
45
+ # becomes the base directives and base narrative for the blueprint.
46
+ # If there are no phase directives, this is the entire blueprint.
30
47
  @base_grafs = []
31
48
  @base_directives = parse_phase(phase_chunks.shift, {}, @base_grafs)
32
49
  @base_directives['full-name'] ||= @base_directives['name']
33
50
 
34
- # The rest of the blueprint, if any, consists of directives and
35
- # narrative for specific phases. The directives for each phase
36
- # include all the base directives, as well as those specific to
37
- # the phase.
51
+ # The rest of the blueprint, if any, consists of directives
52
+ # and narrative for specific phases.
53
+ # The directives for each phase include all the base directives,
54
+ # as well as those specific to the phase.
38
55
  until phase_chunks.empty?
39
56
  phase_name = phase_chunks.shift
40
57
  phase_contents = phase_chunks.shift
@@ -45,9 +62,9 @@ module Litbuild
45
62
  @phase_grafs[phase_name] = grafs
46
63
  end
47
64
 
48
- # Any directives at the beginning of a blueprint are actually a
49
- # file header that should not be considered as part of the
50
- # narrative for it.
65
+ # Any directives at the beginning of a blueprint are
66
+ # actually a file header
67
+ # that should not be considered as part of the narrative for it.
51
68
  @base_grafs.shift while @base_grafs[0].is_a?(Hash)
52
69
  rescue StandardError => e
53
70
  msg = "Cannot parse blueprint starting: #{@text.lines[0..3].join}"
@@ -56,9 +73,51 @@ module Litbuild
56
73
 
57
74
  private
58
75
 
59
- # Parse all directive paragraphs found in blueprint_text. Also add
60
- # all parsed directive paragraphs, and all narrative paragraphs, to
61
- # a collecting parameter.
76
+ # Find all `PARAM[parameter_name]` patterns in the text to parse.
77
+ # Replace each pattern with the parameter value.
78
+ #
79
+ # This must be done before anything else so that the conditional
80
+ # block conditions can be evaluated.
81
+ def substitute_parameters(parameters)
82
+ to_substitute = @text.scan(/PARAM\[([A-Z_]+)\]/).flatten.sort.uniq
83
+ to_substitute.each do |p|
84
+ @text.gsub!(/PARAM\[#{p}\]/, parameters[p])
85
+ end
86
+ end
87
+
88
+ # Find all #IF ... #ENDIF blocks.
89
+ # For each one, either remove it entirely,
90
+ # or remove the #IF and #ENDIF lines and leave the block.
91
+ def resolve_conditional_blocks
92
+ lines = @text.lines
93
+ resolved = []
94
+ in_conditional_block = false
95
+ condition_is_true = nil
96
+ lines.each do |line|
97
+ if in_conditional_block
98
+ if line.match?(/^#ENDIF/)
99
+ in_conditional_block = false
100
+ condition_is_true = nil
101
+ elsif condition_is_true
102
+ resolved << line
103
+ end
104
+ next
105
+ end
106
+ if line.match?(/^#IF/)
107
+ in_conditional_block = true
108
+ left, right = /^#IF (.*) = (.*)$/.match(line)[1..2]
109
+ condition_is_true = (left == right)
110
+ next
111
+ end
112
+ resolved << line
113
+ end
114
+ @text = resolved.join
115
+ end
116
+
117
+ # Parse all directive paragraphs found in blueprint_text.
118
+ # Also add all parsed directive paragraphs,
119
+ # and all narrative paragraphs,
120
+ # to a collecting parameter.
62
121
  def parse_phase(blueprint_text, start_with_directives, graf_collector)
63
122
  directives = JSON.parse(JSON.generate(start_with_directives))
64
123
  paragraphs = blueprint_text.split(/\n\n+/m)
@@ -76,8 +135,8 @@ module Litbuild
76
135
  end
77
136
 
78
137
  # All s6-rc service directories should be tracked in the
79
- # configuration file repository, so add them to
80
- # `configuration-files`.
138
+ # configuration file repository,
139
+ # so add them to `configuration-files`.
81
140
  def handle_servicedirs(directives)
82
141
  cfgs = directives['configuration-files'] || []
83
142
  if (spipes = directives['service-pipeline'])
@@ -114,6 +173,8 @@ module Litbuild
114
173
  graf_directives = Hash.new { |h, k| h[k] = [] }
115
174
  until lines_to_process.empty?
116
175
  directive_line = lines_to_process.shift
176
+ next if directive_line.empty?
177
+
117
178
  md = /^([A-Za-z0-9_-]+): *(.*)/.match(directive_line)
118
179
  unless md
119
180
  raise(Litbuild::ParseError,
@@ -134,11 +195,12 @@ module Litbuild
134
195
  end
135
196
 
136
197
  # Regardless of what kind of directive structure we're dealing with,
137
- # all of the lines that are indented more than the initial directive
138
- # line are part of that directive. This method finds all those
139
- # related lines and strips away the common initial indentation; it
140
- # returns both the related lines and the amount of indentation that
141
- # was removed.
198
+ # all lines that are indented more than the initial directive line
199
+ # are part of that directive.
200
+ # This method finds all those related lines
201
+ # and strips away the common initial indentation;
202
+ # it returns both the related lines
203
+ # and the amount of indentation that was removed.
142
204
  def related_lines(directive_line, lines_to_process)
143
205
  directive_indent = indent_for(directive_line)
144
206
  related = []
@@ -149,9 +211,11 @@ module Litbuild
149
211
  strip_indentation_from_array(related)
150
212
  end
151
213
 
152
- # What kind of directive are we dealing with? Could be a simple
153
- # value (with zero or more continuation lines), or a multiline
154
- # value, or an array, or a subdirective block.
214
+ # What kind of directive are we dealing with?
215
+ # Could be a simple value (with zero or more continuation lines),
216
+ # or a multiline value,
217
+ # or an array,
218
+ # or a subdirective block.
155
219
  def parse_directive_value(firstline_value, other_lines, indentation)
156
220
  if firstline_value == '|'
157
221
  multiline_value(other_lines, indentation)
@@ -167,20 +231,25 @@ module Litbuild
167
231
  end
168
232
 
169
233
  # any time a directive line is indented more than the previous line,
170
- # without being a part of an array or sub-directive or multi-line
171
- # directive, it's a continuation of the previous line.
234
+ # without being a part of an array
235
+ # or sub-directive
236
+ # or multi-line
237
+ # directive,
238
+ # it's a continuation of the previous line.
172
239
  def folded_continuation_lines(first_line, related)
173
240
  stripped = related.map(&:strip)
174
241
  stripped.unshift(first_line)
175
242
  stripped.join(" \\\n ")
176
243
  end
177
244
 
178
- # While parsing multiline values, we want to restore the indentation
179
- # that was previously stripped away -- this is typically used for
180
- # `file` directives, which often appear in multiple directive blocks
181
- # and should be treated as a whole. The indentation for multi-line
182
- # values must be removed *later*, during rendering of scripts and
183
- # documents.
245
+ # While parsing multiline values,
246
+ # we want to restore the indentation
247
+ # that was previously stripped away --
248
+ # this is typically used for `file` directives,
249
+ # which often appear in multiple directive blocks
250
+ # and should be treated as a whole.
251
+ # The indentation for multi-line values must be removed *later*,
252
+ # during rendering of scripts and documents.
184
253
  def multiline_value(lines, indentation)
185
254
  lines_with_indentation = lines.map { |l| (' ' * indentation) + l }
186
255
  "#{lines_with_indentation.join("\n")}\n"
@@ -199,9 +268,10 @@ module Litbuild
199
268
  raise(Litbuild::ParseError, "Problem parsing: #{lines}")
200
269
  end
201
270
 
202
- # It turns out that sub-directives are the easiest thing in the
203
- # world to parse, since we can just take the value and parse it as a
204
- # new directive block.
271
+ # It turns out that sub-directives are
272
+ # the easiest thing in the world to parse,
273
+ # since we can just take the value
274
+ # and parse it as a new directive block.
205
275
  def subdirective_value(lines)
206
276
  parse_lines(lines)
207
277
  end
@@ -16,9 +16,6 @@ module Litbuild
16
16
  # All the actual work is done by Visitors that are handed to the
17
17
  # top-level blueprint target (typically specified on the command line).
18
18
  class Driver
19
- REQUIRED_PARAMS = %w[DOCUMENT_DIR LOGFILE_DIR PATCH_DIR
20
- SCRIPT_DIR TARFILE_DIR WORK_SITE].freeze
21
-
22
19
  def initialize(logfile_namer_class: Litbuild::LogfileNamer)
23
20
  @bplib = BlueprintLibrary.new(logfile_namer_class: logfile_namer_class)
24
21
  end
@@ -16,7 +16,7 @@ module Litbuild
16
16
  'packages'
17
17
  end
18
18
 
19
- def initialize(text:)
19
+ def initialize(text:, parameters:, logfile_namer:, library:)
20
20
  super
21
21
  if phases?
22
22
  phases.each do |p|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Litbuild
4
- VERSION = '1.0.14'
4
+ VERSION = '1.0.16'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: litbuild
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.14
4
+ version: 1.0.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Neumeier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-19 00:00:00.000000000 Z
11
+ date: 2023-11-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A build system based on Knuth's idea of literate programming.
14
14
  email:
@@ -63,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
63
  - !ruby/object:Gem::Version
64
64
  version: '0'
65
65
  requirements: []
66
- rubygems_version: 3.4.7
66
+ rubygems_version: 3.4.22
67
67
  signing_key:
68
68
  specification_version: 4
69
69
  summary: A literate build system