litbuild 1.0.3 → 1.0.11

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: 79441e2b11890ec0e9a3271224f80ed9ca7c40f548fc3ee59bf774a55d3ff54f
4
- data.tar.gz: 77ab5c060afb201d5a0014e92bd8e2ad1352efc8dbc0d37d864992d159a53be9
3
+ metadata.gz: 450a93fb7a60735ed6618b58aff828dd2c6a8846b7664e7916df70ca319f1f4c
4
+ data.tar.gz: 0f7523cd36707b6748f868b6229d69413874503282511929891e1d4ea344d8df
5
5
  SHA512:
6
- metadata.gz: 5f0a0bb98c15066ad8f945a1717ccbfdaa59bb7ac60caa8975459cf62a2601367dab0d68e3e7722999726de22f3d1acbac92a957972574184b430a8ee1d8dbef
7
- data.tar.gz: a366a4a6c91629107a7f9d3868273b8bce8baf26670dc22b4d1edb53d5c31dcbd8fc1e9c2835561f96d576c6cc5a113d6cd261663a12edff7e285085b8d07d0e
6
+ metadata.gz: '08ee5269104135777fb2dd06c225b03854b8b38c61723c0652b98aebc1fef19cad4ded3267249312e8821de764fab8360189216bf50e6b039f442a48cc9ed169'
7
+ data.tar.gz: 4626ce942e4afd88bca32d3613488bd2ac509107c90f467ae0deb3f14c0086ff6fd401647a9173ab5906284a042422802c0dc0fb067e80645217cf70e98e4686
data/bin/lb CHANGED
@@ -4,14 +4,10 @@
4
4
  gem 'litbuild'
5
5
  require 'litbuild'
6
6
  require 'optparse'
7
- require 'ostruct'
8
7
  require 'fileutils'
9
8
 
10
- options = OpenStruct.new
11
-
12
- # defaults:
13
- options.query = nil
14
- options.version = false
9
+ Options = Struct.new(:query, :version)
10
+ options = Options.new(nil, false)
15
11
 
16
12
  OptionParser.new do |opts|
17
13
  opts.banner = 'Usage: lb [options] target'
@@ -3,6 +3,7 @@
3
3
  require 'stringio'
4
4
  require 'litbuild/multi_part_visitor'
5
5
  require 'litbuild/service_dir'
6
+ require 'litbuild/string_indentation'
6
7
 
7
8
  module Litbuild
8
9
  ##
@@ -15,6 +16,8 @@ module Litbuild
15
16
  # blueprint (phase). AsciiDocVisitor can also write a top-level
16
17
  # AsciiDoc document that includes all top-level fragments.
17
18
  class AsciiDocVisitor < MultiPartVisitor
19
+ include StringIndentation
20
+
18
21
  def initialize(parameters:)
19
22
  @parameters = parameters
20
23
  super(directory: @parameters['DOCUMENT_DIR'])
@@ -139,10 +142,9 @@ module Litbuild
139
142
  FileUtils.mkdir_p(location)
140
143
  doc_name = format('%<count>02d-%<doc>s',
141
144
  count: @written[location].size,
142
- doc: blueprint.file_name + '.adoc')
143
- File.open(File.join(location, doc_name), 'w') do |f|
144
- f.write(to_asciidoc(blueprint, extra, summary, block))
145
- end
145
+ doc: "#{blueprint.file_name}.adoc")
146
+ content = to_asciidoc(blueprint, extra, summary, block)
147
+ File.write(File.join(location, doc_name), content)
146
148
  @written[location] << doc_name
147
149
  @all_written_targets << blueprint.target_name
148
150
  end
@@ -180,12 +182,12 @@ module Litbuild
180
182
  extra.call(doc)
181
183
  # If there are any extraneous blank lines in the document, get rid
182
184
  # of them.
183
- doc.string.gsub(/\n\n\n+/, "\n\n").strip + "\n"
185
+ "#{doc.string.gsub(/\n\n\n+/, "\n\n").strip}\n"
184
186
  end
185
187
 
186
188
  def phase_heading(level, blueprint)
187
189
  "[[#{blueprint.name_with_phase},#{blueprint.header_text_with_phase}]]\n" \
188
- "#{level} #{blueprint.header_text_with_phase}\n\n"
190
+ "#{level} #{blueprint.header_text_with_phase}\n\n"
189
191
  end
190
192
 
191
193
  def render_grafs(doc, grafs, block)
@@ -275,7 +277,7 @@ module Litbuild
275
277
  end
276
278
  svc.multiline_files.keys.sort.each do |filename|
277
279
  doc.puts("|#{filename.capitalize} script\nl|")
278
- doc.puts(svc.multiline_files[filename])
280
+ doc.puts(strip_indentation_from_value(svc.multiline_files[filename]))
279
281
  doc.puts
280
282
  end
281
283
  end
@@ -359,7 +361,7 @@ module Litbuild
359
361
  end
360
362
  doc.puts('[source,indent=0]')
361
363
  doc.puts('----')
362
- doc.puts(file['content'])
364
+ doc.puts(strip_indentation_from_value(file['content']))
363
365
  doc.puts('----')
364
366
  end
365
367
 
@@ -372,7 +374,7 @@ module Litbuild
372
374
  doc.puts("=== #{filename}\n\n")
373
375
  doc.puts('[source,indent=0]')
374
376
  doc.puts('----')
375
- doc.puts(files[filename])
377
+ doc.puts(strip_indentation_from_string(files[filename].join))
376
378
  doc.puts('----')
377
379
  end
378
380
  end
@@ -397,9 +399,8 @@ module Litbuild
397
399
  end
398
400
  if !package.phases? || for_phase
399
401
  write_environment_row(doc, package)
400
- if package.build_dir
402
+ package.build_dir &&
401
403
  doc.puts("|Build Directory| `#{package.build_dir}`")
402
- end
403
404
  end
404
405
  doc.puts("|===\n\n")
405
406
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'litbuild/service_dir'
4
4
  require 'litbuild/source_code_manager'
5
+ require 'litbuild/string_indentation'
5
6
  require 'litbuild/visitor'
6
7
 
7
8
  module Litbuild
@@ -13,6 +14,8 @@ module Litbuild
13
14
  # phase) has already been written to any directory, BashScriptVisitor
14
15
  # will ignore subsequent requests to write that blueprint (phase).
15
16
  class BashScriptVisitor < Visitor
17
+ include StringIndentation
18
+
16
19
  INSTALL_GID = 9999
17
20
 
18
21
  attr_reader :blueprint_dir
@@ -104,6 +107,7 @@ module Litbuild
104
107
  skip_line(f)
105
108
  f.puts("trap 'echo UTTER FAILURE on line $LINENO' ERR")
106
109
  f.puts('set -e -v')
110
+ f.puts('cd $(dirname $0)')
107
111
  skip_line(f)
108
112
  write_components(location: script_dir, script: f)
109
113
  f.puts('set +v')
@@ -120,13 +124,11 @@ module Litbuild
120
124
  FileUtils.mkdir_p(location)
121
125
  script_name = format('%<count>02d-%<script>s',
122
126
  count: @written[location].size,
123
- script: blueprint.file_name + '.sh')
127
+ script: "#{blueprint.file_name}.sh")
124
128
  script_path = File.join(location, script_name)
125
- File.open(script_path, 'w') do |f|
126
- f.write(to_bash_script(blueprint, block))
127
- end
129
+ File.write(script_path, to_bash_script(blueprint, block))
128
130
  FileUtils.chmod('ugo+x', script_path)
129
- envcmds = environment_commands(blueprint).reject { |c| c =~ /^mkdir/ }
131
+ envcmds = environment_commands(blueprint).grep_v(/^mkdir/)
130
132
  unless envcmds.empty?
131
133
  envscript = File.join(location, "env_#{blueprint.file_name}.sh")
132
134
  File.open(envscript, 'w') do |f|
@@ -180,7 +182,7 @@ module Litbuild
180
182
  blueprint.files.keys.sort.map do |name|
181
183
  accum = StringIO.new
182
184
  accum.puts("cat > #{name} <<'LBEOF'")
183
- accum.puts(blueprint.files[name].string)
185
+ accum.puts(strip_indentation_from_string(blueprint.files[name].string))
184
186
  accum.puts('LBEOF')
185
187
  accum.string
186
188
  end
@@ -214,11 +216,11 @@ module Litbuild
214
216
  return [] if running_as_root
215
217
 
216
218
  raw_sudo_cmds = @all_commands.select do |c|
217
- c =~ /sudo / && c.lines.size < 2
219
+ c.include?('sudo ') && c.lines.size < 2
218
220
  end.uniq
219
221
  sudo_cmds = raw_sudo_cmds.map do |c|
220
222
  sudoed_cmd = c.sub(/^.*sudo (.*)$/, '\\1')
221
- sudoed_cmd = sudoed_cmd.sub(/;.*$/, '') if sudoed_cmd.match?(/;/)
223
+ sudoed_cmd = sudoed_cmd.sub(/;.*$/, '') if sudoed_cmd.include?(';')
222
224
  sudoed_cmd = convert_to_absolute(sudoed_cmd)
223
225
  sudoed_cmd.gsub(/([,:=\\])/, '\\\\\1')
224
226
  end
@@ -241,17 +243,39 @@ module Litbuild
241
243
  build_location = dir_for_build(package)
242
244
 
243
245
  if package.build_dir
244
- render_command(script, "mkdir -p #{build_location}", '/dev/null')
246
+ render_command(script, "mkdir -p #{build_location}")
245
247
  skip_line(script)
246
248
  end
247
249
 
250
+ # When actually running stage commands: if something goes wrong we
251
+ # want to try one more time without MAKEFLAGS set. This addresses
252
+ # the case where parallelism breaks builds, and also addresses
253
+ # ephemeral compiler errors that can crop up when using an
254
+ # emulated virtual machine.
255
+ #
256
+ # The simplest way of doing that, with proper `set -e` behavior,
257
+ # is to have a separate script for each stage and possibly run
258
+ # each script twice. (Shell functions don't work the same way.
259
+ # Bash is complicated.) So here we are going to write out
260
+ # temporary scripts that we can then invoke.
261
+ render_command(script, 'scriptdir=$(mktemp -d pkg.XXXXXX)')
262
+ render_command(script, 'trap "rm -rf $scriptdir" EXIT')
263
+ skip_line(script)
248
264
  Package::BUILD_STAGES.each do |stage|
265
+ path = "$scriptdir/#{stage}.sh"
266
+ render_command(script, "cat > #{path} <<'LBEOF'")
267
+ render_command(script, '#!/bin/bash')
268
+ render_command(script, 'set -e')
249
269
  log = package.logfile(stage, phase)
250
270
  render_in_dir(script, build_location) do
251
271
  package.build_commands(stage).each do |command|
252
272
  render_command(script, command, log)
253
273
  end
254
274
  end
275
+ render_command(script, 'LBEOF')
276
+ render_command(script, "chmod 755 #{path}")
277
+ exec_cmd = "#{path} >> #{log} 2>&1"
278
+ render_command(script, "#{exec_cmd} || MAKEFLAGS='' #{exec_cmd}")
255
279
  end
256
280
  render_restart_trailer(script, restart_file, package.version)
257
281
  end
@@ -311,7 +335,6 @@ module Litbuild
311
335
  log = package.logfile('pkgusr')
312
336
  pkgusr_dir = "~#{pkgusr}"
313
337
  package.directives['configuration-files'] ||= []
314
- package.directives['configuration-files'] << "~#{pkgusr}/options"
315
338
  restart_file = ".#{package.active_phase || 'default'}"
316
339
  render_restart_header(script, restart_file, package.version, pkgusr_dir)
317
340
  pkgusr_srcdir = File.join(pkgusr_dir, package.name_and_version)
@@ -366,7 +389,7 @@ module Litbuild
366
389
  package.build_dir && options.puts("export build_dir=#{package.build_dir}")
367
390
  render_intree_commands(package, options)
368
391
  Package::BUILD_STAGES.each do |stage|
369
- options.puts("function #{stage}_commands()\n{")
392
+ options.puts("function #{stage}_commands\n{")
370
393
  cmds = package.build_commands(stage)
371
394
  if cmds.empty?
372
395
  options.puts(':')
@@ -425,11 +448,14 @@ module Litbuild
425
448
  script.puts('fi')
426
449
  end
427
450
 
428
- def render_command(script, command, log)
451
+ def render_command(script, command, log = nil)
429
452
  unfolded_command = command.gsub(/ ?\\\n */, ' ')
430
453
  @all_commands << unfolded_command
431
- if unfolded_command.match?(/>/)
432
- # redirecting output of command, can't put stdout in log.
454
+
455
+ # If a command's output is being redirected, it can't be logged.
456
+ # And if logging is being done elsewhere, we don't need to do it
457
+ # here.
458
+ if unfolded_command.include?('>') || log.nil?
433
459
  script.puts(unfolded_command)
434
460
  else
435
461
  script.puts("#{unfolded_command} >> #{log} 2>&1")
@@ -485,9 +511,8 @@ module Litbuild
485
511
  sdirs = spipe['servicedirs']
486
512
  sdirs.each_with_index do |sdir, i|
487
513
  render_service_dir(script, sdir)
488
- if i == sdirs.size - 1
514
+ i == sdirs.size - 1 &&
489
515
  script.puts("echo #{pname} > #{sdir['name'].first}/pipeline-name")
490
- end
491
516
  if i < sdirs.size - 1
492
517
  next_svc = sdirs[i + 1]['name'].first
493
518
  script.puts("echo #{next_svc} > #{sdir['name'].first}/producer-for")
@@ -512,7 +537,7 @@ module Litbuild
512
537
  end
513
538
  multiline = sd.multiline_files
514
539
  deps = sd.dependencies
515
- multiline['dependencies'] = deps.join("\n") unless deps.empty?
540
+ multiline['dependencies'] = [deps.join("\n")] unless deps.empty?
516
541
  multiline.keys.sort.each do |filename|
517
542
  # I always terminate here documents in litbuild-generated
518
543
  # scripts with "LBEOF", partly because I'm thinking "Litbuild
@@ -520,7 +545,7 @@ module Litbuild
520
545
  # Shia LaBoeuf, and then I remember the Rob Cantor song of
521
546
  # that name and giggle.
522
547
  script.puts("cat > #{sd.name}/#{filename} <<'LBEOF'")
523
- script.puts(multiline[filename])
548
+ script.puts(strip_indentation_from_value(multiline[filename]))
524
549
  script.puts('LBEOF')
525
550
  end
526
551
  env = sd.env
@@ -537,7 +562,10 @@ module Litbuild
537
562
  cfgs = blueprint['configuration-files']
538
563
  return unless cfgs
539
564
 
540
- render_command(script, "cfggit add #{cfgs.join(' ')}", log)
565
+ unless cfgs.empty?
566
+ cmd = "cfggit add #{cfgs.join(' ')}"
567
+ render_command(script, cmd, log)
568
+ end
541
569
  render_command(script, 'cfggit stageall', log)
542
570
  bp = "#{blueprint.class.name.split('::').last} #{blueprint.name}"
543
571
  cmd = "cfggit as-default -m 'Configuration files for #{bp}'"
@@ -553,11 +581,12 @@ module Litbuild
553
581
  # now, if the value contains ' -- backslash-quote everything (incl spaces)
554
582
  # if the value contains " -- single-quote the whole thing
555
583
  # if the value contains other punctuation -- double-quote the whole thing
556
- if oneline.match?(/'/)
584
+ case oneline
585
+ when /'/
557
586
  oneline.gsub(/([\\ "'`$])/, '\\\\\1')
558
- elsif oneline.match?(/"/)
587
+ when /"/
559
588
  "'#{oneline}'"
560
- elsif oneline.match?(/[\\ `]/)
589
+ when /[\\ `]/
561
590
  "\"#{oneline}\""
562
591
  else
563
592
  oneline
@@ -7,8 +7,17 @@ require 'litbuild/logfile_namer'
7
7
  require 'litbuild/visitor'
8
8
 
9
9
  module Litbuild
10
+ # Blueprints are described in the `doc` directory.
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.
16
+ #
17
+ # After parsing, the narrative is found in the `grafs` variables and
18
+ # directives are found in `directive` hashes.
10
19
  class Blueprint
11
- class <<self
20
+ class << self
12
21
  # This should simply be the name of the directory where blueprints
13
22
  # of each specific type are expected to be found by Driver.
14
23
  def self.directory_name
@@ -16,9 +25,7 @@ module Litbuild
16
25
  end
17
26
  end
18
27
 
19
- attr_reader :active_phase
20
- attr_reader :base_grafs
21
- attr_writer :logfile_namer
28
+ attr_reader :active_phase, :base_grafs, :logfile_namer
22
29
 
23
30
  def self.descendants
24
31
  ObjectSpace.each_object(Class).select { |c| c < self }
@@ -180,7 +187,7 @@ module Litbuild
180
187
  # to avoid including two versions of the dependency.
181
188
  def deduped_dependency_names
182
189
  dep_names = self['depends-on'].clone || []
183
- deps_with_phase = dep_names.select { |dep| dep.match?(/::/) }
190
+ deps_with_phase = dep_names.select { |dep| dep.include?('::') }
184
191
  deps_with_phase.each do |dep|
185
192
  dep_without_phase = dep.split('::')[0]
186
193
  dep_names.delete(dep_without_phase)
@@ -209,7 +216,7 @@ module Litbuild
209
216
  def resolve_all(parameters, something)
210
217
  case something
211
218
  when Hash
212
- something.values.each { |element| resolve_all(parameters, element) }
219
+ something.each_value { |element| resolve_all(parameters, element) }
213
220
  when Array
214
221
  something.each { |element| resolve_all(parameters, element) }
215
222
  when String
@@ -220,9 +227,8 @@ module Litbuild
220
227
  def resolve(parameters:, a_string:)
221
228
  params = a_string.scan(/PARAM\[([A-Z_]+)\]/).flatten.sort.uniq
222
229
  params.each do |p|
223
- unless parameters[p]
230
+ parameters[p] ||
224
231
  raise(ParameterMissing, "Parameter #{p} is not defined")
225
- end
226
232
 
227
233
  a_string.gsub!(/PARAM\[#{p}\]/, parameters[p])
228
234
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'litbuild/commands.rb'
4
- require 'litbuild/narrative.rb'
5
- require 'litbuild/package.rb'
6
- require 'litbuild/section.rb'
3
+ require 'litbuild/commands'
4
+ require 'litbuild/narrative'
5
+ require 'litbuild/package'
6
+ require 'litbuild/section'
7
7
 
8
8
  module Litbuild
9
9
  # BlueprintLibrary initializes and sets configuration parameters for
@@ -33,9 +33,7 @@ module Litbuild
33
33
  def blueprint_for(target:)
34
34
  name, phase = split_name_and_phase(target: target)
35
35
  bp = blueprints[name]
36
- unless bp
37
- raise(UnknownBlueprint, "Blueprint #{name} not found in library")
38
- end
36
+ bp || raise(UnknownBlueprint, "Blueprint #{name} not found in library")
39
37
 
40
38
  bp.for_phase(phase)
41
39
  end
@@ -46,7 +44,7 @@ module Litbuild
46
44
  def dependencies_for(blueprint)
47
45
  dep_names = blueprint.deduped_dependency_names
48
46
  dep_names.map do |dep|
49
- if dep.match?(/::/) # Explicit phase specified
47
+ if dep.include?('::') # Explicit phase specified
50
48
  blueprint_for(target: dep)
51
49
  else
52
50
  bp = blueprint_for(target: dep)
@@ -64,7 +62,7 @@ module Litbuild
64
62
  # split a target name, like linux::headers, into a blueprint name
65
63
  # (linux) and a phase name (headers).
66
64
  def split_name_and_phase(target:)
67
- if target.match?(/::/)
65
+ if target.include?('::')
68
66
  target.strip.match(/(.*)::(.*)/)[1..2]
69
67
  else
70
68
  [target.strip, nil]
@@ -82,13 +80,15 @@ module Litbuild
82
80
  # works around the issue, whatever it is.
83
81
  return unless blueprint_type.name
84
82
 
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]
87
+
85
88
  Dir.glob("./#{blueprint_type.directory_name}/*.txt").each do |a_file|
86
89
  bp_text = File.read(a_file)
87
90
  begin
88
- # All blueprints have a name and a full-name. If a blueprint
89
- # has no `name:` or `full-name:` directive, they will be
90
- # provided at load time.
91
- %w[name full-name].each do |directive|
91
+ automatic_directives.each do |directive|
92
92
  unless bp_text.match?(/^#{directive}:/)
93
93
  bp_text = "#{directive}: #{File.basename(a_file, '.txt')}\n\n" +
94
94
  bp_text
@@ -1,20 +1,18 @@
1
1
  # frozen_string_literal: false
2
2
 
3
3
  require 'litbuild/errors'
4
+ require 'litbuild/string_indentation'
4
5
  require 'json'
5
6
 
6
7
  module Litbuild
7
8
  # This is a kludgy hand-built parser. Blueprint structure is not (at
8
9
  # least, at this point) complicated enough that I can see any point in
9
10
  # defining a grammar and using a parser generator. The structure of
10
- # blueprint directives is described informally in
11
- # `doc/blueprints.txt` (and blueprint-type-specific files under `doc`
12
- # as well).
13
- #
14
- # tl;dr: each paragraph can be either directives or narrative.
15
- # Narrative is AsciiDoc and does not get parsed or transformed.
16
- # Directives are basically a simplified version of YAML.
11
+ # blueprints is described informally in `doc/blueprints.txt` (and
12
+ # blueprint-type-specific files under `doc` as well).
17
13
  class BlueprintParser
14
+ include StringIndentation
15
+
18
16
  # _directives_ are for use in scripts. _grafs_ are for use in
19
17
  # documents.
20
18
  attr_reader :base_directives, :phase_directives, :base_grafs, :phase_grafs
@@ -30,9 +28,8 @@ module Litbuild
30
28
  # blueprint. If there are no phase directives, this is the entire
31
29
  # blueprint, obvs.
32
30
  @base_grafs = []
33
- base = parse_phase(phase_chunks.shift, {}, @base_grafs)
34
- base['full-name'] ||= base['name']
35
- @base_directives = base
31
+ @base_directives = parse_phase(phase_chunks.shift, {}, @base_grafs)
32
+ @base_directives['full-name'] ||= @base_directives['name']
36
33
 
37
34
  # The rest of the blueprint, if any, consists of directives and
38
35
  # narrative for specific phases. The directives for each phase
@@ -42,13 +39,15 @@ module Litbuild
42
39
  phase_name = phase_chunks.shift
43
40
  phase_contents = phase_chunks.shift
44
41
  grafs = []
45
- @phase_directives[phase_name] = parse_phase(phase_contents, base, grafs)
42
+ @phase_directives[phase_name] = parse_phase(phase_contents,
43
+ @base_directives,
44
+ grafs)
46
45
  @phase_grafs[phase_name] = grafs
47
46
  end
48
47
 
49
48
  # Any directives at the beginning of a blueprint are actually a
50
- # file header that should not be rendered as part of the narrative
51
- # for it.
49
+ # file header that should not be considered as part of the
50
+ # narrative for it.
52
51
  @base_grafs.shift while @base_grafs[0].is_a?(Hash)
53
52
  rescue StandardError => e
54
53
  msg = "Cannot parse blueprint starting: #{@text.lines[0..3].join}"
@@ -97,7 +96,7 @@ module Litbuild
97
96
  end
98
97
 
99
98
  def directives?(paragraph)
100
- paragraph.split(' ').first =~ /^[a-z-]+:/
99
+ paragraph.split.first =~ /^[a-z-]+:/
101
100
  end
102
101
 
103
102
  def add_directives(directives, parsed)
@@ -122,15 +121,13 @@ module Litbuild
122
121
  end
123
122
  directive_name = md[1]
124
123
  firstline_value = md[2]
125
- value_lines = related_lines(directive_line, lines_to_process)
126
- value = parse_directive_value(firstline_value, value_lines)
127
- if value == "''"
128
- # two literal apostrophes is a special case, we treat it as an
129
- # empty string. Probably ought to document that.
130
- graf_directives[directive_name] << ''
131
- else
132
- graf_directives[directive_name] << value
133
- end
124
+ value_lines, indent = related_lines(directive_line, lines_to_process)
125
+ value = parse_directive_value(firstline_value, value_lines, indent)
126
+ graf_directives[directive_name] << if value == "''"
127
+ ''
128
+ else
129
+ value
130
+ end
134
131
  graf_directives[directive_name].flatten!
135
132
  end
136
133
  graf_directives
@@ -139,7 +136,9 @@ module Litbuild
139
136
  # Regardless of what kind of directive structure we're dealing with,
140
137
  # all of the lines that are indented more than the initial directive
141
138
  # line are part of that directive. This method finds all those
142
- # related lines and strips away the common initial indentation.
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.
143
142
  def related_lines(directive_line, lines_to_process)
144
143
  directive_indent = indent_for(directive_line)
145
144
  related = []
@@ -147,16 +146,15 @@ module Litbuild
147
146
  (indent_for(lines_to_process[0]) > directive_indent)
148
147
  related << lines_to_process.shift
149
148
  end
150
- common_indent = related.map { |l| indent_for(l) }.min
151
- related.map { |l| l.slice(common_indent..-1) }
149
+ strip_indentation_from_array(related)
152
150
  end
153
151
 
154
152
  # What kind of directive are we dealing with? Could be a simple
155
153
  # value (with zero or more continuation lines), or a multiline
156
154
  # value, or an array, or a subdirective block.
157
- def parse_directive_value(firstline_value, other_lines)
155
+ def parse_directive_value(firstline_value, other_lines, indentation)
158
156
  if firstline_value == '|'
159
- multiline_value(other_lines)
157
+ multiline_value(other_lines, indentation)
160
158
  elsif other_lines.empty?
161
159
  firstline_value
162
160
  elsif other_lines[0].match?(/^ *- /)
@@ -177,8 +175,15 @@ module Litbuild
177
175
  stripped.join(" \\\n ")
178
176
  end
179
177
 
180
- def multiline_value(lines)
181
- lines.join("\n") + "\n"
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.
184
+ def multiline_value(lines, indentation)
185
+ lines_with_indentation = lines.map { |l| (' ' * indentation) + l }
186
+ "#{lines_with_indentation.join("\n")}\n"
182
187
  end
183
188
 
184
189
  def array_value(lines)
@@ -186,8 +191,8 @@ module Litbuild
186
191
  until lines.empty?
187
192
  array_member = lines.shift
188
193
  firstline_value = /^- *(.*)$/.match(array_member)[1]
189
- related = related_lines(array_member, lines)
190
- value << parse_directive_value(firstline_value, related)
194
+ related, indentation = related_lines(array_member, lines)
195
+ value << parse_directive_value(firstline_value, related, indentation)
191
196
  end
192
197
  value
193
198
  rescue StandardError
@@ -200,11 +205,5 @@ module Litbuild
200
205
  def subdirective_value(lines)
201
206
  parse_lines(lines)
202
207
  end
203
-
204
- # Utility method to find the amount of blank space at the beginning
205
- # of a line.
206
- def indent_for(line)
207
- /^([[:blank:]]*).*/.match(line)[1].size
208
- end
209
208
  end
210
209
  end
@@ -26,9 +26,8 @@ module Litbuild
26
26
  protected
27
27
 
28
28
  def add_file_content(directive)
29
- unless directive['name'] && directive['content']
29
+ (directive['name'] && directive['content']) ||
30
30
  raise(InvalidDirective, 'file directive missing name or content')
31
- end
32
31
 
33
32
  content = @files[directive['name'].first] ||= StringIO.new
34
33
  content.puts(directive['content'].first)
@@ -8,7 +8,7 @@ module Litbuild
8
8
  end
9
9
 
10
10
  def path_for(blueprint, phase = nil, stage = nil)
11
- count = format('%03d', @counter)
11
+ count = format('%<counter>03d', counter: @counter)
12
12
  @counter += 1
13
13
  file_name = build_name(count, blueprint, phase, stage)
14
14
  File.join(@log_dir, file_name)
@@ -17,7 +17,7 @@ module Litbuild
17
17
  protected
18
18
 
19
19
  def build_name(*elements)
20
- elements.uniq.compact.join('-') + '.log'
20
+ "#{elements.uniq.compact.join('-')}.log"
21
21
  end
22
22
  end
23
23
  end
@@ -29,7 +29,6 @@ module Litbuild
29
29
 
30
30
  protected
31
31
 
32
- attr_reader :other_parts
33
- attr_reader :appendices
32
+ attr_reader :other_parts, :appendices
34
33
  end
35
34
  end
@@ -35,8 +35,8 @@ module Litbuild
35
35
 
36
36
  blueprints = self['blueprints'].clone || []
37
37
  if $DEBUG
38
- warn("Explicit blueprints for section #{name}:" \
39
- " #{blueprints.join(', ')}")
38
+ warn("Explicit blueprints for section #{name}: " \
39
+ "#{blueprints.join(', ')}")
40
40
  end
41
41
  blueprints << automatic_inclusions
42
42
  deduped = blueprints.flatten.uniq
@@ -56,8 +56,8 @@ module Litbuild
56
56
  end
57
57
  end
58
58
  if $DEBUG
59
- warn("Automatically-added blueprints for section #{name}:" \
60
- " #{auto_adds.sort.join(', ')}")
59
+ warn("Automatically-added blueprints for section #{name}: " \
60
+ "#{auto_adds.sort.join(', ')}")
61
61
  end
62
62
  auto_adds.sort
63
63
  end
@@ -67,14 +67,12 @@ module Litbuild
67
67
  end
68
68
  copy_commands = []
69
69
  package.tar_files_needed.each do |filename|
70
- unless pkgusr_file_available?(package, filename)
71
- copy_commands << "cp #{find_tarfile(filename)} ~#{pkgusr}/src"
72
- end
70
+ pkgusr_file_available?(package, filename) ||
71
+ (copy_commands << "cp #{find_tarfile(filename)} ~#{pkgusr}/src")
73
72
  end
74
73
  package.patch_files.each do |filename|
75
- unless pkgusr_file_available?(package, filename)
76
- copy_commands << "cp #{find_file(filename)} ~#{pkgusr}/patches"
77
- end
74
+ pkgusr_file_available?(package, filename) ||
75
+ (copy_commands << "cp #{find_file(filename)} ~#{pkgusr}/patches")
78
76
  end
79
77
  mkdir_commands + copy_commands.sort
80
78
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: false
2
+
3
+ module Litbuild
4
+ module StringIndentation
5
+ # Utility method to find the amount of blank space at the beginning
6
+ # of a line.
7
+ def indent_for(line)
8
+ /^([[:blank:]]*).*/.match(line)[1].size
9
+ end
10
+
11
+ # Utility method to strip the common indentation from all strings in
12
+ # an array. Returns both the stripped strings and the amount of
13
+ # indentation removed.
14
+ def strip_indentation_from_array(strings)
15
+ common_indent = strings.map { |l| indent_for(l) }.min
16
+ [strings.map { |l| l.slice(common_indent..-1) }, common_indent]
17
+ end
18
+
19
+ # Utility method to strip the common indentation from all lines of a
20
+ # multi-line string. (Does not return the amount of indentation
21
+ # removed.)
22
+ def strip_indentation_from_string(string)
23
+ strip_indentation_from_array(string.lines)[0]
24
+ end
25
+
26
+ # Utility method to strip the common indentation from all lines of a
27
+ # directive value (which is expected to be an array containing a
28
+ # single string element)
29
+ def strip_indentation_from_value(value)
30
+ strip_indentation_from_string(value[0])
31
+ end
32
+ end
33
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Litbuild
4
- VERSION = '1.0.3'
4
+ VERSION = '1.0.11'
5
5
  end
metadata CHANGED
@@ -1,40 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: litbuild
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Neumeier
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
- cert_chain:
11
- - |
12
- -----BEGIN CERTIFICATE-----
13
- MIIEMDCCApigAwIBAgIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZicmV0
14
- dC9EQz1mcmVlc2EvREM9b3JnMB4XDTE5MDkwMTEyNDkxMFoXDTIwMDgzMTEyNDkx
15
- MFowITEfMB0GA1UEAwwWYnJldHQvREM9ZnJlZXNhL0RDPW9yZzCCAaIwDQYJKoZI
16
- hvcNAQEBBQADggGPADCCAYoCggGBALKrZnh07ME+wnUpu3EYsKisqaD68WomtNpt
17
- pOIPUWtQnRD3iVKmPfIsCrwGbU1NQDohiwz0SdLlLVDHIovwZB/j9sI5tzSfltas
18
- 5dNgN4mgtURqlILYNmuyDbOOsWtOec9Nz7OJfvV6oat7m4prf+rU0j8JtzCAsFNT
19
- B/IdLSqJlJEQ4UMeHPaC06RjOJJJOXSE+yh0Sm2dHjNnUyFnrzfHehMMFJ0wpL42
20
- RFaxRNGp/U9fn/h94lXz8h42ksoWXWG8RrsnRA3Olsu6uWQJsggnMbD7zzHlQ9Xf
21
- 0cV9ePbXmyKm2EfvcoVLBybimPmvYxrXOOxblbIMFl3yWX+YfJKrEsLb5F54RSao
22
- N4wy76RNF+d7m2yarhiqPf1dm8kzPRzJmhLLMpPa89N3mQaQtC+jrVEME6ahLNhl
23
- lnxLd3zcT7ExrHJQQnQgei5AsJe80Noq9K2+8QjLSYDSNsc80SuBczqMeybHxVVA
24
- 4CJKSU+amFYp/Z414cJr9AG+/xal8wIDAQABo3MwcTAJBgNVHRMEAjAAMAsGA1Ud
25
- DwQEAwIEsDAdBgNVHQ4EFgQUtedZFs4wsrrlPms2Gt+1jV7JYYwwGwYDVR0RBBQw
26
- EoEQYnJldHRAZnJlZXNhLm9yZzAbBgNVHRIEFDASgRBicmV0dEBmcmVlc2Eub3Jn
27
- MA0GCSqGSIb3DQEBCwUAA4IBgQAfjb44P0zp86q4cvN8Wv/VEyvVLEiTT/DFWmER
28
- UBMPB1lIUxxgCs6MqxpRK8icX4IRb/W9qeC2nIBsmY7u4Xoj3JiaOLmoJImXt+tO
29
- KdGUu7ygymVZ7991XaAf4dpOcVYMmUDqS+G7mo/nbHBT4440AG2SSL02Nj7zDfn8
30
- Ut0XU+6UT3WxpBIA1o/BTmLkPvQhkOEMv7sWBWHdvS0eSxNTSfXpwkjIYpXMsUIg
31
- BbgMcxnxUIv5Y+Xb/TIbrRYqiDtqsDT+vsadKk3qjN0bx7vCVdibh2cfGTJA9Spe
32
- pi1q/NcnHXSeMFJ4iWyp6lyZxo+B7KIFkgD/irWbBoyZYpbMYJexasmoVuFgyvND
33
- VBt/HnbucMCrdU4EqbBQHFimTIyvOaI12/TaU4362smlgZhZdnlHyBeOUBGzDSLX
34
- PvrrfEkjwo+u4dPBTaO5ZBa4qsFE5bK/1l6d4AVV5Yi5NohUwmpp1bFFCGPqvzVA
35
- bhee2x0YS1uGTnADpv2GLkmNMIA=
36
- -----END CERTIFICATE-----
37
- date: 2019-09-14 00:00:00.000000000 Z
10
+ cert_chain: []
11
+ date: 2022-12-19 00:00:00.000000000 Z
38
12
  dependencies: []
39
13
  description: A build system based on Knuth's idea of literate programming.
40
14
  email:
@@ -65,14 +39,16 @@ files:
65
39
  - lib/litbuild/service_dir.rb
66
40
  - lib/litbuild/source_code_manager.rb
67
41
  - lib/litbuild/source_files_visitor.rb
42
+ - lib/litbuild/string_indentation.rb
68
43
  - lib/litbuild/url_visitor.rb
69
44
  - lib/litbuild/version.rb
70
45
  - lib/litbuild/visitor.rb
71
46
  homepage: http://git.freesa.org/freesa/litbuild
72
47
  licenses:
73
48
  - GPL-3.0
74
- metadata: {}
75
- post_install_message:
49
+ metadata:
50
+ rubygems_mfa_required: 'true'
51
+ post_install_message:
76
52
  rdoc_options: []
77
53
  require_paths:
78
54
  - lib
@@ -80,15 +56,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
80
56
  requirements:
81
57
  - - ">="
82
58
  - !ruby/object:Gem::Version
83
- version: '2.6'
59
+ version: '3.0'
84
60
  required_rubygems_version: !ruby/object:Gem::Requirement
85
61
  requirements:
86
62
  - - ">="
87
63
  - !ruby/object:Gem::Version
88
64
  version: '0'
89
65
  requirements: []
90
- rubygems_version: 3.0.3
91
- signing_key:
66
+ rubygems_version: 3.2.3
67
+ signing_key:
92
68
  specification_version: 4
93
69
  summary: A literate build system
94
70
  test_files: []
checksums.yaml.gz.sig DELETED
Binary file
data.tar.gz.sig DELETED
Binary file
metadata.gz.sig DELETED
@@ -1,4 +0,0 @@
1
- H�8�,�Bj��
2
- 8-ik�[���ѩ�o����P})2~�ѷ�0����V������ϥt�
3
- ��N�Q-)�ET����?^5C�1P<�H7?� w��T>Nui�$Y5�f��u2O�����S;BQ2�()�bI�ǩ5M[�s��
4
- �/"s���XkC��23[�R)ioM��c,7� &K N�cu���J�'L��=�my�fӵW����b=R���+n��wK���