litbuild 1.0.9 → 1.0.14

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: 021106eb1915ada36b41382da16fdde8d9c2762d3e250dc0cbdb5067c7432bcf
4
- data.tar.gz: a42ebf4088c9c1f7b9ae2d3d4f238ec2e9610c17c6fd07da1e0af6a5938c71dd
3
+ metadata.gz: 3f420862b3a3dd758ef05aed2b717defe89a48a34944b25b3af4042a0d47a749
4
+ data.tar.gz: bfb4c6364c57b9b9220d0bd43113f4a8b2ff0639b106b675a9e8af3c85982eab
5
5
  SHA512:
6
- metadata.gz: 93da197d79732d1944ec9821f2d567c76962408923e68a2642789a4408176ab341dcf6e2fb61b2274413b98272e2d4f3cf979c38b20d11186d7f80a6a8e90238
7
- data.tar.gz: 2a38bdce9e787c5685ccaf92fca51cffc16da80c900694701fc6a169aef9c9e8797eedbf8fe53d317e2129000126bff9076d69ba4da0f0bba086630e367b6928
6
+ metadata.gz: 22838c4497fdb70a5140507dcc0a10f511f9751bd36197ec3dc88a8c5e7bc6fda27b8678bd34f46ea7d21b9f06c944e52b3bcd7ffee00713d9aa34a56d168387
7
+ data.tar.gz: 01f7b1eab7fe96e96fbd04dd446743259786bb54a8556f379627c13153c0e917ea6af368fdc4bb3c4f201666f32d78380a9c416ee238e3e39a4c07305b200edd
@@ -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'])
@@ -274,7 +277,7 @@ module Litbuild
274
277
  end
275
278
  svc.multiline_files.keys.sort.each do |filename|
276
279
  doc.puts("|#{filename.capitalize} script\nl|")
277
- doc.puts(svc.multiline_files[filename])
280
+ doc.puts(strip_indentation_from_value(svc.multiline_files[filename]))
278
281
  doc.puts
279
282
  end
280
283
  end
@@ -358,7 +361,7 @@ module Litbuild
358
361
  end
359
362
  doc.puts('[source,indent=0]')
360
363
  doc.puts('----')
361
- doc.puts(file['content'])
364
+ doc.puts(strip_indentation_from_value(file['content']))
362
365
  doc.puts('----')
363
366
  end
364
367
 
@@ -371,7 +374,7 @@ module Litbuild
371
374
  doc.puts("=== #{filename}\n\n")
372
375
  doc.puts('[source,indent=0]')
373
376
  doc.puts('----')
374
- doc.puts(files[filename])
377
+ doc.puts(strip_indentation_from_string(files[filename].join))
375
378
  doc.puts('----')
376
379
  end
377
380
  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
@@ -164,9 +167,14 @@ module Litbuild
164
167
  script.string
165
168
  end
166
169
 
170
+ # Timestamps are written with default format plus
171
+ # seconds-since-epoch (in parentheses), to make it as easy as
172
+ # possible to calculate how long things take.
173
+ DATECMD = "date '+%a %b %e %H:%M:%S %Z %Y (%s)'"
174
+
167
175
  def write_components(location:, script:)
168
176
  @written[location].map do |target|
169
- script.puts("echo \"At $(date): Beginning #{target}:\"")
177
+ script.puts("echo \"At $(#{DATECMD}): Beginning #{target}:\"")
170
178
  script.puts("./#{target}")
171
179
  end
172
180
  end
@@ -179,7 +187,7 @@ module Litbuild
179
187
  blueprint.files.keys.sort.map do |name|
180
188
  accum = StringIO.new
181
189
  accum.puts("cat > #{name} <<'LBEOF'")
182
- accum.puts(blueprint.files[name].string)
190
+ accum.puts(strip_indentation_from_string(blueprint.files[name].string))
183
191
  accum.puts('LBEOF')
184
192
  accum.string
185
193
  end
@@ -336,7 +344,7 @@ module Litbuild
336
344
  render_restart_header(script, restart_file, package.version, pkgusr_dir)
337
345
  pkgusr_srcdir = File.join(pkgusr_dir, package.name_and_version)
338
346
  render_add_package_user(package, script, log)
339
- @scm.copy_source_files_commands(package).each do |cp_command|
347
+ @scm.copy_files_commands(package).each do |cp_command|
340
348
  render_command(script, cp_command, log)
341
349
  end
342
350
  script.puts("export LB_SOURCE_DIR=#{quote(pkgusr_srcdir)}")
@@ -502,6 +510,7 @@ module Litbuild
502
510
  def render_service_pipeline(script, spipe)
503
511
  pname = spipe['name'].first
504
512
  spipe['bundle']&.each do |bundle|
513
+ script.puts("mkdir -p #{bundle}")
505
514
  script.puts("grep -q '^#{pname}$' #{bundle}/contents || " \
506
515
  "echo #{pname} >> #{bundle}/contents")
507
516
  end
@@ -534,7 +543,7 @@ module Litbuild
534
543
  end
535
544
  multiline = sd.multiline_files
536
545
  deps = sd.dependencies
537
- multiline['dependencies'] = deps.join("\n") unless deps.empty?
546
+ multiline['dependencies'] = [deps.join("\n")] unless deps.empty?
538
547
  multiline.keys.sort.each do |filename|
539
548
  # I always terminate here documents in litbuild-generated
540
549
  # scripts with "LBEOF", partly because I'm thinking "Litbuild
@@ -542,7 +551,7 @@ module Litbuild
542
551
  # Shia LaBoeuf, and then I remember the Rob Cantor song of
543
552
  # that name and giggle.
544
553
  script.puts("cat > #{sd.name}/#{filename} <<'LBEOF'")
545
- script.puts(multiline[filename])
554
+ script.puts(strip_indentation_from_value(multiline[filename]))
546
555
  script.puts('LBEOF')
547
556
  end
548
557
  env = sd.env
@@ -7,6 +7,15 @@ 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
20
  class << self
12
21
  # This should simply be the name of the directory where blueprints
@@ -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}"
@@ -122,8 +121,8 @@ 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)
124
+ value_lines, indent = related_lines(directive_line, lines_to_process)
125
+ value = parse_directive_value(firstline_value, value_lines, indent)
127
126
  graf_directives[directive_name] << if value == "''"
128
127
  ''
129
128
  else
@@ -137,7 +136,9 @@ module Litbuild
137
136
  # Regardless of what kind of directive structure we're dealing with,
138
137
  # all of the lines that are indented more than the initial directive
139
138
  # line are part of that directive. This method finds all those
140
- # 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.
141
142
  def related_lines(directive_line, lines_to_process)
142
143
  directive_indent = indent_for(directive_line)
143
144
  related = []
@@ -145,16 +146,15 @@ module Litbuild
145
146
  (indent_for(lines_to_process[0]) > directive_indent)
146
147
  related << lines_to_process.shift
147
148
  end
148
- common_indent = related.map { |l| indent_for(l) }.min
149
- related.map { |l| l.slice(common_indent..-1) }
149
+ strip_indentation_from_array(related)
150
150
  end
151
151
 
152
152
  # What kind of directive are we dealing with? Could be a simple
153
153
  # value (with zero or more continuation lines), or a multiline
154
154
  # value, or an array, or a subdirective block.
155
- def parse_directive_value(firstline_value, other_lines)
155
+ def parse_directive_value(firstline_value, other_lines, indentation)
156
156
  if firstline_value == '|'
157
- multiline_value(other_lines)
157
+ multiline_value(other_lines, indentation)
158
158
  elsif other_lines.empty?
159
159
  firstline_value
160
160
  elsif other_lines[0].match?(/^ *- /)
@@ -175,8 +175,15 @@ module Litbuild
175
175
  stripped.join(" \\\n ")
176
176
  end
177
177
 
178
- def multiline_value(lines)
179
- "#{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"
180
187
  end
181
188
 
182
189
  def array_value(lines)
@@ -184,8 +191,8 @@ module Litbuild
184
191
  until lines.empty?
185
192
  array_member = lines.shift
186
193
  firstline_value = /^- *(.*)$/.match(array_member)[1]
187
- related = related_lines(array_member, lines)
188
- value << parse_directive_value(firstline_value, related)
194
+ related, indentation = related_lines(array_member, lines)
195
+ value << parse_directive_value(firstline_value, related, indentation)
189
196
  end
190
197
  value
191
198
  rescue StandardError
@@ -198,11 +205,5 @@ module Litbuild
198
205
  def subdirective_value(lines)
199
206
  parse_lines(lines)
200
207
  end
201
-
202
- # Utility method to find the amount of blank space at the beginning
203
- # of a line.
204
- def indent_for(line)
205
- /^([[:blank:]]*).*/.match(line)[1].size
206
- end
207
208
  end
208
209
  end
@@ -131,11 +131,16 @@ module Litbuild
131
131
  end
132
132
 
133
133
  # otherwise, the default version goes right *before* the *first
134
- # directive of the next stage* (which will always be present).
134
+ # directive of the next stage* (which *should* always be present)...
135
135
  next_stage = STAGE_DIRECTIVES[STAGE_DIRECTIVES.index(stg) + 1]
136
136
  first_idx = grafs.index do |graf|
137
137
  graf.respond_to?(:key) && graf.key?(next_stage)
138
138
  end
139
+
140
+ # ...but if, for whatever reason, we haven't found a place to
141
+ # put the default directives, just put them at the beginning.
142
+ first_idx ||= 0
143
+
139
144
  grafs.insert(first_idx, stg => to_add[stg])
140
145
  end
141
146
  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
@@ -13,7 +13,8 @@ module Litbuild
13
13
  #
14
14
  # Note, SourceCodeManager does not recurse into additional levels of
15
15
  # subdirectories -- it turns out that makes the test suite really
16
- # slow.
16
+ # slow, and recursive search is not useful to me, so I'm just
17
+ # skipping it.
17
18
  class SourceCodeManager
18
19
  def initialize(*dirs)
19
20
  all_pkgfiles = dirs.map do |d|
@@ -55,13 +56,39 @@ module Litbuild
55
56
  end
56
57
 
57
58
  ##
58
- # Package Users expect to have tarfiles for the top-level package
59
- # and any in-tree packages in their `src` directory, and patches in
60
- # their `patches` directory. This produces commands that copy needed
61
- # files from the TARFILE_DIR and PATCH_DIR to those directories, if
62
- # not already present there.
63
- def copy_source_files_commands(package)
59
+ # Typically, Package Users expect to have tarfiles for the top-level
60
+ # package and any in-tree packages in their `src` directory, and
61
+ # patches in their `patches` directory. This method arranges things
62
+ # that way: it produces commands that copy all the necessary files
63
+ # from TARFILE_DIR and/or PATCH_DIR to the destination directory,
64
+ # skipping any that are already present where the Package Users
65
+ # build script expects to find them.
66
+ #
67
+ # Binary package files are an exception to the typical case. A
68
+ # binary package file has name `binary-#{packagename}.tar.lz` (no
69
+ # version number, and always lzip-compressed), and contains the
70
+ # files produced by the compilation and installation process. If
71
+ # such a file is present in the Package User home directory, the
72
+ # build script simply unpacks it and does nothing else.
73
+ #
74
+ # So: if a binary package file is present in the tarfile directory,
75
+ # this method simply emits a command to copy it to the Package User
76
+ # home directory; and if there is already a binary package file
77
+ # present in the Package User home directory, this method does
78
+ # nothing.
79
+ def copy_files_commands(package)
64
80
  pkgusr = pkgusr_name(package)
81
+
82
+ # copy binary file if available, then return
83
+ binfile = "binary-#{package.name}.tar.lz"
84
+ return ["cp #{find_file(binfile)} ~#{pkgusr}"] \
85
+ if @available_files.detect { |f| /#{binfile}/ =~ f }
86
+
87
+ # do nothing if binary file is present in home dir already
88
+ return [] if homedir(package) && File.exist?(
89
+ File.join(homedir(package), binfile)
90
+ )
91
+
65
92
  mkdir_commands = %w[src patches].map do |dir|
66
93
  "mkdir -p ~#{pkgusr}/#{dir}"
67
94
  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.9'
4
+ VERSION = '1.0.14'
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.9
4
+ version: 1.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Neumeier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-25 00:00:00.000000000 Z
11
+ date: 2023-02-19 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:
@@ -39,6 +39,7 @@ files:
39
39
  - lib/litbuild/service_dir.rb
40
40
  - lib/litbuild/source_code_manager.rb
41
41
  - lib/litbuild/source_files_visitor.rb
42
+ - lib/litbuild/string_indentation.rb
42
43
  - lib/litbuild/url_visitor.rb
43
44
  - lib/litbuild/version.rb
44
45
  - lib/litbuild/visitor.rb
@@ -62,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
63
  - !ruby/object:Gem::Version
63
64
  version: '0'
64
65
  requirements: []
65
- rubygems_version: 3.3.3
66
+ rubygems_version: 3.4.7
66
67
  signing_key:
67
68
  specification_version: 4
68
69
  summary: A literate build system