litbuild 1.0.21 → 1.0.27

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: 22c6c387b304ec0c1abb563a8cb3f5a8812a52433c82a2cc9c4322e202b6ea65
4
- data.tar.gz: e590d33d71e4c70a7a4411f67b11d17fb46f8c4846b6c1ac4fdae91dfc9205e5
3
+ metadata.gz: cce41178693bb69ed91330ddebbedeb7db27b71263c52e9841010e328e2e9b05
4
+ data.tar.gz: 05de3849a236d875dd817b829f37fc461f177fa38860002a39a9e3f8f6db3845
5
5
  SHA512:
6
- metadata.gz: 3fd317ab3c29b1b37ddac395ba27ab95d304c4b052c80bebab9250acb60b11946e51f432fdc39c761a392a0d96714a21981c5adfbd726c0654d30b520ad33eb5
7
- data.tar.gz: 37e5f89cce214d9a2fcd4ce4ce44b00aa44d5da9cfc6ce564f7a142c9b2c8bdde1eeeb822b2e125e3a3b450bb78db61017f9eaa94b400baa0668699be29dba8a
6
+ metadata.gz: 8a658ecf5db92962af7f400e343017b6018ba70b1992a026f60dc7d6949762b749655eafe2fe1d8d3eb109ad23da5ad2db4510c17c712d02f6fecd5c96475944
7
+ data.tar.gz: e190668e4fcca7ccdebc422ae33d8b202777ec0d82887af45de2c3041d74e9f92f16148063f36770531a88e572192e2ea3d80a3ab333a18fb3c881da1ac41266
data/README CHANGED
@@ -5,58 +5,62 @@ The idea of literate programming was invented by Donald Knuth. The basic
5
5
  notion -- at least, the way I think of it -- is that the _primary_ users
6
6
  of the source code of a program are the people who are reading the
7
7
  source code to acquire an understanding of how it works (often, because
8
- they want to modify it), and that the transformation of the source code
8
+ they want to modify it); and that the transformation of the source code
9
9
  into an executable program that can be run, or the actual use of that
10
10
  program, is a _secondary_ purpose of the code.
11
11
 
12
- Because of this prioritization, most of the attention and energy
13
- invested in writing the program is in a narrative discussion of how the
14
- program works and what the purpose of each component (function
15
- declaration, subroutine or coroutine, or whatever) is within that
16
- narrative.
12
+ Because of this ranking of priorities, when writing a program in
13
+ literate programming style, most of the attention and energy is invested
14
+ in a narrative discussion of how the program works and what the purpose
15
+ of each component (function declaration, subroutine or coroutine, or
16
+ whatever) is within that narrative.
17
17
 
18
18
  Automated build tools -- such as make, maven, and jam -- are great,
19
- because they allow developers on a project to focus their attention on
20
- the project codebase _per se,_ and let the build system worry about how
21
- to compile and test it. There are lots of build tools, reflecting a wide
22
- variety of design goals and value systems. Regardless of which tool is
23
- used, though, I have noticed that once a software project reaches a
24
- certain level of complexity, the automated build becomes similarly
25
- complex -- to the point that it is a major undertaking to figure out how
26
- it actually works.
19
+ because they allow developers working on a project to focus their
20
+ attention on the project codebase _per se,_ and let the build system
21
+ worry about how to compile and test it. There are lots of build tools,
22
+ reflecting a wide variety of design goals and value systems. Regardless
23
+ of which tool is used, though, I have noticed that once a software
24
+ project reaches a certain level of complexity, the build process becomes
25
+ similarly complex -- to the point that it is a major undertaking to
26
+ figure out how it actually works.
27
27
 
28
28
  That makes it difficult to diagnose and correct problems with the build
29
- system when they arise, for the same reason that any other
30
- incomprehensible software code is hard to work with effectively... and
31
- since nobody pays any attention to the build machinery until it stops
32
- working for some reason, I've also noticed that it's common for everyone
33
- on a team or project to forget how it works in between those times. At
34
- best, there's one person who knows how it all works, and he or she gets
35
- saddled with the job of fixing it or tweaking it when that's necessary.
29
+ system when they arise, for the same reasons that any other
30
+ incomprehensible code is hard to work with effectively... and I've also
31
+ noticed that, in most cases, nobody pays any attention to the build
32
+ machinery until it stops working for some reason, with the result that
33
+ it's common for everyone on a team or project to have forgotten how the
34
+ build system works by the point that something goes wrong and it needs
35
+ to be repaired or modified. At best, there's one person who knows how it
36
+ all works, and he or she gets saddled with the job of fixing it or
37
+ tweaking it when that's necessary.
36
38
 
37
39
  Litbuild is an attempt to bring together the ideas of literate
38
- programming and automated build tools. My goal is to make it easy to
39
- write build scripts in such a way that they _tell a story_ about how to
40
- build a project (regardless of how big or complex that project is),
41
- focusing primarily on describing that build process with enough clarity
42
- that other people can understand it -- while still providing enough
43
- structure to that story that the litbuild system can produce scripts
44
- that will perform an automated build of the project.
40
+ programming and automated build tools.
41
+
42
+ My goal is to make it easy to write build scripts in such a way that
43
+ they _tell a story_ about how to build a project (regardless of how big
44
+ or complex that project is), focusing primarily on describing that build
45
+ process with enough clarity that other people can understand it -- while
46
+ still providing enough structure to that story that the litbuild system
47
+ can produce scripts that will perform an automated build of the project.
45
48
 
46
49
  The original literate programming systems, WEB and CWEB, have two modes
47
50
  of operation: you can run the literate source code through a `tangle`
48
51
  program to produce source code in a traditional programming language
49
52
  like Pascal or C (which you can then feed to a compiler to produce a
50
53
  program), or you can run it through a `weave` program that produces
51
- document source (which you can send through a typesetting system like
54
+ document source (which you can process using a typesetting system like
52
55
  TeX or Docbook).
53
56
 
54
- Litbuild is essentially a processor for a domain-specific language. It
55
- takes, as input, human-readable files -- referred to as "blueprints" --
56
- that consist of narrative text written with AsciiDoc markup,
57
- interspersed with command blocks and other directives, just like the
58
- literate source in WEB and CWEB. Litbuild has a simpler interface,
59
- though: when it is run, it always generates both a set of bash scripts
57
+ Litbuild is simpler; it has only one mode of operation. It takes, as
58
+ input, human-readable files -- referred to as "blueprints" -- that
59
+ consist of narrative text interspersed with command blocks and other
60
+ directives, just like the literate source in WEB and CWEB. In the case
61
+ of litbuild, the narrative text is written in AsciiDoc, and the command
62
+ blocks and other directives are mostly shell commands. When litbuild
63
+ processes a set of blueprints, it generates both a set of bash scripts
60
64
  that will execute the actual build process and _also_ AsciiDoc-formatted
61
65
  documentation files that can be run through a document production
62
66
  toolchain to produce HTML or PDF or whatever other documentation format
@@ -75,25 +79,25 @@ package installed...
75
79
 
76
80
  Or both!
77
81
 
82
+
78
83
  Using Litbuild
79
84
  ==============
80
85
 
81
86
  The litbuild gem provides a simple driver script, `lb`, that loads all
82
87
  the blueprint files it finds in a directory tree and then generates
83
- output files for the blueprint target specified on the command line. You
84
- can only specify one blueprint as a target; if you want more than one
85
- thing to be built, create a Section blueprint that talks about all the
86
- blueprints you want to build and how they fit together. If the target
87
- you want to build is a phase within a blueprint, specify it as
88
+ output files for the blueprint target specified on the command line.
89
+
90
+ You can only specify one blueprint as a target; if you want more than
91
+ one thing to be built, create a Section blueprint that talks about all
92
+ the blueprints you want to build and how they fit together. If the
93
+ target you want to build is a phase within a blueprint, specify it as
88
94
  `blueprint::phase_name`; if the phase has a space in it, you can of
89
95
  course quote the command line argument like `"blueprint::phase name"`.
90
96
 
91
- Blueprints can refer to parameterized values. It is expected that
92
- default values for all these parameters will be provided (by convention,
93
- I generally put all the default values into a "configuration" narrative
94
- blueprint). To override any parameter value from the default, set an
95
- environment variable with its name before running the `lb` driver
96
- program.
97
+ Blueprints can contain references to parameterized values. It is
98
+ expected that default values for all these parameters will be provided
99
+ in the blueprints. To override any parameter value, set an environment
100
+ variable with its name before running the `lb` driver program.
97
101
 
98
102
  To use the lb driver to build a complete cross-toolchain as part of the
99
103
  Cross-Building Linux process, for example, you might use something like:
@@ -102,15 +106,15 @@ Cross-Building Linux process, for example, you might use something like:
102
106
 
103
107
  It's not strictly _necessary_ to use Section blueprints to perform a
104
108
  complete build for a complex target, because blueprints can specify what
105
- other bleuprints they depend on and litbuild will pull in whatever other
106
- blueprints are needed to resolve those dependencies. If all dependencies
107
- are correctly specified in all blueprints, all you really need to do is
108
- request the final target! But it's still a good idea to write a Section
109
- for any situation where the various components of a build story need to
110
- have an overall framing narrative or discussion to make them
111
- comprehensible, or when all the parts of a build need to share a common
112
- configuration or environment -- which is the case for most complicated
113
- processes.
109
+ other blueprints they depend on and litbuild will pull in all the
110
+ blueprints that are needed to resolve those dependencies. If all
111
+ dependencies are correctly specified in all blueprints, all you really
112
+ need to do is request the final target! But it's still a good idea to
113
+ write a Section for any situation where the various components of a
114
+ build story need to have an overall framing narrative or discussion to
115
+ make them comprehensible, or when all the parts of a build need to share
116
+ a common configuration or environment -- which is the case for most
117
+ complicated processes.
114
118
 
115
119
  If any commands in the generated scripts appear to be run under `sudo`,
116
120
  litbuild has a feature that can help to run those scripts
@@ -122,13 +126,34 @@ with a full absolute path, or must exist in a specific set of default
122
126
  directories: /bin, /sbin, /usr/bin, /usr/sbin, /usr/local/bin, and
123
127
  /usr/local/sbin.
124
128
 
125
- Debugging Blueprints
126
- ====================
129
+ The AsciiDoc markup language supports "admonition" paragraphs, which are
130
+ set apart from the rest of the narrative with distinctive formatting.
131
+ There are a few styles of admonition defined in AsciiDoc: `NOTE`,
132
+ `TIP`, `IMPORTANT`, `CAUTION`, and `WARNING`. Litbuild provides some
133
+ extra visibility for `WARNING` admonitions: in addition to rendering
134
+ them into the AsciiDoc output files, litbuild will write the text of
135
+ these admonitions to the standard error stream as it processes them. If
136
+ you're writing a blueprint, and you want to call the user's attention to
137
+ something important even if they neglect to generate and read the
138
+ resulting build narrative, you might find it helpful to include a
139
+ `WARNING` admonition.
140
+
141
+ (The support for WARNING admonitions works for single-paragraph
142
+ admonitions where the paragraph starts with `WARNING:`, and it works for
143
+ at least some admonition blocks with a `[WARNING]` label. If you want to
144
+ use this feature, it's probably a good idea to try running litbuild on
145
+ your blueprint(s) and make sure that the output is what you would like
146
+ it to be.)
147
+
148
+
149
+ Debugging Blueprints And Increasing Verbosity
150
+ =============================================
127
151
 
128
152
  If litbuild is rejecting your blueprints with an error message and you
129
153
  can't figure out why, you can produce a great deal of debugging
130
154
  information by setting the environment variable RUBYOPT to `--debug`.
131
155
 
156
+
132
157
  Cross-Building Linux
133
158
  ====================
134
159
 
@@ -136,13 +161,13 @@ The litbuild program is developed in conjunction with a set of
136
161
  blueprints called "Cross-Building Linux," or CBL for short; this defines
137
162
  the build process used for Little Blue Linux. A lot of the features in
138
163
  litbuild are present for the convenience of CBL -- like the s6-rc
139
- service directory and pipeline logic, the ability to produce
164
+ service directory and pipeline directives, the ability to produce
140
165
  package-user scripts, and the invocation of configuration file
141
166
  repository scripts. If you're using litbuild for some other purpose, and
142
167
  don't want to make use of those features, you might want to eliminate
143
168
  them.
144
169
 
145
- We're thinking of eventually rewriting litbuild as a C program to
146
- eliminate its dependency on ruby; if we do that, we'll probably provide
147
- a compile-time configuration option that allows features like these to
148
- be compiled out of the program.
170
+ I'm thinking of eventually rewriting litbuild as a C program to
171
+ eliminate its dependency on ruby; if I do that, perhaps I'll provide a
172
+ compile-time configuration option that allows features like these to be
173
+ compiled out of the program.
@@ -23,6 +23,7 @@ module Litbuild
23
23
  super(directory: @parameters['DOCUMENT_DIR'])
24
24
  @written = Hash.new { |hash, key| hash[key] = [] }
25
25
  @all_written_targets = []
26
+ @warnings = []
26
27
  end
27
28
 
28
29
  def visit_commands(commands:)
@@ -114,6 +115,10 @@ module Litbuild
114
115
  end
115
116
  end
116
117
 
118
+ def warning_admonitions
119
+ @warnings.empty? ? nil : @warnings.join("\n\n")
120
+ end
121
+
117
122
  private
118
123
 
119
124
  ##
@@ -193,11 +198,29 @@ module Litbuild
193
198
  def render_grafs(doc, grafs, block)
194
199
  grafs.each do |graf|
195
200
  case graf
196
- when String then doc.puts(graf)
197
- when Hash then handle_directives(doc, graf, block)
201
+ when String
202
+ doc.puts(graf)
203
+ when Hash
204
+ handle_directives(doc, graf, block)
198
205
  end
199
206
  doc.puts
200
207
  end
208
+ string_grafs = grafs.select { |graf| graf.is_a?(String) }
209
+ complex_warning_collector = nil
210
+ until string_grafs.empty?
211
+ graf = string_grafs.shift.strip
212
+ if complex_warning_collector
213
+ complex_warning_collector << graf
214
+ if graf.end_with?('====')
215
+ @warnings << complex_warning_collector.join("\n\n")
216
+ complex_warning_collector = nil
217
+ end
218
+ elsif graf.start_with?('WARNING:')
219
+ @warnings << graf
220
+ elsif graf.start_with?("[WARNING]\n")
221
+ complex_warning_collector = [graf]
222
+ end
223
+ end
201
224
  end
202
225
 
203
226
  ##
@@ -243,7 +266,9 @@ module Litbuild
243
266
  def write_servicepipe(doc, pipedefs)
244
267
  pipedefs.each do |pipedef|
245
268
  head = ".Service Pipeline: `#{pipedef['name'].first}`"
246
- head += " (in bundle `#{pipedef['bundle'].first}`)" if pipedef['bundle']
269
+ unless pipedef['bundle'].empty?
270
+ head += " (in bundle `#{pipedef['bundle'].first}`)"
271
+ end
247
272
  doc.puts(head)
248
273
  doc.puts("[%autowidth,cols=\"d,d\",caption=]\n|===\n\n")
249
274
  pipedef['servicedirs'].each_with_index do |svcdir, idx|
@@ -18,6 +18,17 @@ module Litbuild
18
18
 
19
19
  INSTALL_GID = 9999
20
20
 
21
+ # Timestamps are written with default format plus
22
+ # seconds-since-epoch (in parentheses), to make it as easy as
23
+ # possible to calculate how long things take.
24
+ DATECMD = "date '+%a %b %e %H:%M:%S %Z %Y (%s)'"
25
+
26
+ # A set of standard program directories is used to find
27
+ # absolute paths.
28
+ SUDOPATH = %w[/bin /sbin
29
+ /usr/bin /usr/sbin
30
+ /usr/local/bin /usr/local/sbin].freeze
31
+
21
32
  attr_reader :blueprint_dir
22
33
 
23
34
  def initialize(parameters:)
@@ -27,8 +38,7 @@ module Litbuild
27
38
  @all_written_targets = []
28
39
  @all_commands = []
29
40
  @blueprint_dir = quote(File.expand_path('.'))
30
- @scm = SourceCodeManager.new(@parameters['TARFILE_DIR'],
31
- @parameters['PATCH_DIR'])
41
+ @scm = SourceCodeManager.new(@parameters['MATERIALS_DIR'])
32
42
  end
33
43
 
34
44
  def visit_commands(commands:)
@@ -121,10 +131,7 @@ module Litbuild
121
131
  skip_line(file)
122
132
  file.puts('if [ -n "$LITBUILDDBDIR" ]')
123
133
  file.puts('then')
124
- file.puts(' if [ -d "${LITBUILDDBDIR}" -a -w "${LITBUILDDBDIR}" ]')
125
- file.puts(' then')
126
- file.puts(' mkdir -p "${LITBUILDDBDIR}/BUILD-LOG"')
127
- file.puts(' fi')
134
+ file.puts(' mkdir -p "${LITBUILDDBDIR}/BUILD-LOG" || echo nevermind')
128
135
  file.puts('fi')
129
136
  skip_line(file)
130
137
  file.puts("begin_time=$(date '+%a %b %e %H:%M:%S %Z %Y (%s)')")
@@ -136,7 +143,7 @@ module Litbuild
136
143
  skip_line(file)
137
144
  file.puts("complete_time=$(date '+%a %b %e %H:%M:%S %Z %Y (%s)')")
138
145
  file.puts('complete_seconds=$(date +%s)')
139
- file.puts('elapsed_time=$((complete_seconds - begin_seconds))')
146
+ file.puts('elapsed=$((complete_seconds - begin_seconds))')
140
147
  skip_line(file)
141
148
  bldir = '"${LITBUILDDBDIR}/BUILD-LOG"'
142
149
  file.puts("if [ -d #{bldir} -a -w #{bldir} ]")
@@ -145,7 +152,7 @@ module Litbuild
145
152
  file.puts(" echo 'Blueprint: #{target}' > $lbfile")
146
153
  file.puts(' echo "Started: $begin_time" >> $lbfile')
147
154
  file.puts(' echo "Completed: $complete_time" >> $lbfile')
148
- file.puts(' echo "Elapsed: $elapsed_time" >> $lbfile')
155
+ file.puts(' echo "Elapsed: $elapsed" >> $lbfile')
149
156
  write_toplevel_parameters(file)
150
157
  file.puts('fi')
151
158
  skip_line(file)
@@ -209,16 +216,15 @@ module Litbuild
209
216
  script.string
210
217
  end
211
218
 
212
- # Timestamps are written with default format plus
213
- # seconds-since-epoch (in parentheses), to make it as easy as
214
- # possible to calculate how long things take.
215
- DATECMD = "date '+%a %b %e %H:%M:%S %Z %Y (%s)'"
216
-
217
219
  def write_components(location:, script:)
218
220
  @written[location].map do |target|
219
221
  script.puts("echo \"At $(#{DATECMD}): Beginning #{target}:\"")
222
+ script.puts('bp_begin=$(date +%s)')
220
223
  script.puts("./#{target}")
221
- script.puts("echo \"At $(#{DATECMD}): Completed #{target}:\"")
224
+ script.puts('bp_comp=$(date +%s)')
225
+ script.puts('bp_elapsed=$((bp_comp - bp_begin))')
226
+ complete = "At $(#{DATECMD}): Completed #{target} ($bp_elapsed sec)"
227
+ script.puts("echo \"#{complete}\"")
222
228
  end
223
229
  end
224
230
 
@@ -236,15 +242,11 @@ module Litbuild
236
242
  end
237
243
  end
238
244
 
239
- def running_as_root
245
+ def running_as_root?
240
246
  uid = `id -u`.strip
241
247
  uid == '0'
242
248
  end
243
249
 
244
- SUDOPATH = %w[/bin /sbin
245
- /usr/bin /usr/sbin
246
- /usr/local/bin /usr/local/sbin].freeze
247
-
248
250
  def convert_to_absolute(command)
249
251
  return command if command.start_with?('/')
250
252
 
@@ -261,7 +263,7 @@ module Litbuild
261
263
  end
262
264
 
263
265
  def sudoers_entries
264
- return [] if running_as_root
266
+ return [] if running_as_root?
265
267
 
266
268
  raw_sudo_cmds = @all_commands.select do |c|
267
269
  c.include?('sudo ') && c.lines.size < 2
@@ -415,8 +417,9 @@ module Litbuild
415
417
  if (aiar = package['after-install-as-root'])
416
418
  aiar.each { |cmd| render_command(script, cmd, log) }
417
419
  end
420
+ render_command(script, "cfggit add ~#{package.pkgusr_name}/options", log)
418
421
  render_cfgrepo_trailer(script, package, log)
419
- render_command(script, 'set_install_dirs', log)
422
+ render_command(script, 'set-install-dirs', log)
420
423
  render_command(script, 'ldconfig', log)
421
424
  end
422
425
 
@@ -424,7 +427,7 @@ module Litbuild
424
427
  pkgusr = package.value('package-user')
425
428
  desc = pkgusr['description'].first || package.value('full_name')
426
429
  render_command(script,
427
- "add_package_user '#{desc}' #{package.pkgusr_name}",
430
+ "add-package-user '#{desc}' #{package.pkgusr_name}",
428
431
  log)
429
432
  end
430
433
 
@@ -631,13 +634,14 @@ module Litbuild
631
634
 
632
635
  # quote a string suitably for a bash script
633
636
  def quote(value)
634
- # if value contains embedded backslash and newline characters, get
635
- # rid of those first.
637
+ # if value contains embedded backslash and newline characters,
638
+ # get rid of those first.
636
639
  oneline = value.gsub(/\\\n /m, '')
637
640
 
638
- # now, if the value contains ' -- backslash-quote everything (incl spaces)
639
- # if the value contains " -- single-quote the whole thing
640
- # if the value contains other punctuation -- double-quote the whole thing
641
+ # now...
642
+ # if the value contains ': backslash-quote everything (incl spaces)
643
+ # if the value contains ": single-quote the whole thing
644
+ # if the value contains other punctuation: double-quote the whole thing
641
645
  case oneline
642
646
  when /'/
643
647
  oneline.gsub(/([\\ "'`$])/, '\\\\\1')
@@ -14,14 +14,17 @@ module Litbuild
14
14
  # and uses config parameters from *the environment*
15
15
  # (or the default values defined in blueprints).
16
16
  class BlueprintLibrary
17
- REQUIRED_PARAMS = %w[DOCUMENT_DIR LOGFILE_DIR PATCH_DIR
18
- SCRIPT_DIR TARFILE_DIR WORK_SITE].freeze
17
+ REQUIRED_PARAMS = %w[DOCUMENT_DIR LOGFILE_DIR MATERIALS_DIR
18
+ SCRIPT_DIR WORK_SITE].freeze
19
19
 
20
20
  attr_reader :blueprints, :parameters
21
21
 
22
22
  def initialize(logfile_namer_class:)
23
+ warn('Resolving parameters') if $DEBUG
23
24
  @parameters = resolve_parameter_values
25
+ warn('Loading and parsing blueprints') if $DEBUG
24
26
  @blueprints = load_and_parse_all_blueprints(logfile_namer_class)
27
+ warn('Finished loading blueprints') if $DEBUG
25
28
  end
26
29
 
27
30
  def blueprint_for(target:)
@@ -81,11 +84,14 @@ module Litbuild
81
84
 
82
85
  blueprints = {}
83
86
  blueprint_files_by_type.each do |bp_type, bp_files|
87
+ warn("Parsing blueprints of type #{bp_type}") if $DEBUG
84
88
  bp_files.each do |bp_file|
89
+ warn("Parsing blueprint: #{bp_file}") if $DEBUG
85
90
  bp_text = load_and_add_automatic_directives(bp_file)
86
91
  begin
87
92
  bp = bp_type.new(text: bp_text, parameters: @parameters,
88
93
  logfile_namer: log_namer, library: self)
94
+ warn("Parsed as #{bp.name}") if $DEBUG
89
95
  if blueprints[bp.name]
90
96
  raise(DuplicateBlueprint,
91
97
  "Duplicate blueprint #{bp.name} found in #{bp_file}")
@@ -103,8 +109,8 @@ module Litbuild
103
109
  # All blueprints have a name and a full-name.
104
110
  # If a blueprint has no `name:` or `full-name:` directive,
105
111
  # they will be provided at load time
106
- # (the basename of the blueprint file is used for either
107
- # or both missing directives)
112
+ # (the basename of the blueprint file is used for any
113
+ # missing directives)
108
114
  def load_and_add_automatic_directives(filename)
109
115
  text = File.read(filename)
110
116
  name = File.basename(filename, '.txt')
@@ -27,10 +27,18 @@ module Litbuild
27
27
  class BlueprintParser
28
28
  include StringIndentation
29
29
 
30
- # _directives_ are for use in scripts.
31
- # _grafs_ are for use in documents.
30
+ # _directives_ are for use in scripts
31
+ # (and often are rendered in specific ways in documents).
32
+ # _grafs_ (short for "paragraphs") are for use only in documents.
32
33
  attr_reader :base_directives, :phase_directives, :base_grafs, :phase_grafs
33
34
 
35
+ # Litbuild actually creates two BlueprintParsers per blueprint:
36
+ # the first time, it is given only `file_text`,
37
+ # and the result of that operation is used to find parameters.
38
+ # Once all parameters have been found,
39
+ # another set of BlueprintParsers is created
40
+ # with both `file_text` and `parameters`.
41
+ #
34
42
  def initialize(file_text:, parameters: nil)
35
43
  @text = file_text.dup
36
44
  substitute_parameters(parameters) if parameters
@@ -43,7 +51,7 @@ module Litbuild
43
51
  # The first part of the blueprint,
44
52
  # before any phase directive,
45
53
  # becomes the base directives and base narrative for the blueprint.
46
- # If there are no phase directives, this is the entire blueprint.
54
+ # (If there are no phase directives, that is the entire blueprint.)
47
55
  @base_grafs = []
48
56
  @base_directives = parse_phase(phase_chunks.shift, {}, @base_grafs)
49
57
  @base_directives['full-name'] ||= @base_directives['name']
@@ -62,10 +70,12 @@ module Litbuild
62
70
  @phase_grafs[phase_name] = grafs
63
71
  end
64
72
 
65
- # Any directives at the beginning of a blueprint are
66
- # actually a file header
73
+ # Any directives at the beginning of a blueprint
74
+ # constitute a file header
67
75
  # that should not be considered as part of the narrative for it.
68
76
  @base_grafs.shift while @base_grafs[0].is_a?(Hash)
77
+ rescue Litbuild::Error => e
78
+ raise(e)
69
79
  rescue StandardError => e
70
80
  msg = "Cannot parse blueprint starting: #{@text.lines[0..3].join}"
71
81
  raise(Litbuild::ParseError, "#{msg} -- #{e}")
@@ -76,11 +86,15 @@ module Litbuild
76
86
  # Find all `PARAM[parameter_name]` patterns in the text to parse.
77
87
  # Replace each pattern with the parameter value.
78
88
  #
79
- # This must be done before anything else so that the conditional
80
- # block conditions can be evaluated.
89
+ # This must be done before anything else,
90
+ # so that any conditional block conditions can be evaluated.
81
91
  def substitute_parameters(parameters)
82
92
  to_substitute = @text.scan(/PARAM\[([A-Z_]+)\]/).flatten.sort.uniq
83
93
  to_substitute.each do |p|
94
+ unless parameters[p]
95
+ msg = "Parameter \"#{p}\" is referenced but not defined."
96
+ raise(Litbuild::ParameterMissing, msg)
97
+ end
84
98
  @text.gsub!(/PARAM\[#{p}\]/, parameters[p])
85
99
  end
86
100
  end
@@ -134,7 +148,9 @@ module Litbuild
134
148
  directives
135
149
  end
136
150
 
137
- # All s6-rc service directories should be tracked in the
151
+ # s6-rc service directories
152
+ # (if there are any)
153
+ # should be tracked in the
138
154
  # configuration file repository,
139
155
  # so add them to `configuration-files`.
140
156
  def handle_servicedirs(directives)
@@ -212,7 +228,8 @@ module Litbuild
212
228
  end
213
229
 
214
230
  # What kind of directive are we dealing with?
215
- # Could be a simple value (with zero or more continuation lines),
231
+ # It could be a simple value
232
+ # (with zero or more continuation lines),
216
233
  # or a multiline value,
217
234
  # or an array,
218
235
  # or a subdirective block.
@@ -230,16 +247,35 @@ module Litbuild
230
247
  end
231
248
  end
232
249
 
233
- # any time a directive line is indented more than the previous line,
250
+ # Any time a directive line is indented more than the previous line,
234
251
  # without being a part of an array
235
252
  # or sub-directive
236
- # or multi-line
237
- # directive,
253
+ # or multi-line directive,
238
254
  # it's a continuation of the previous line.
255
+ # For AsciiDoc, we want to wind up with something
256
+ # formatted like the original,
257
+ # but will still work if copy-pasted into bash windows.
258
+ # So we'll embed backslashes at the end of each line,
259
+ #
260
+ # To make the indentation more-or-less correct,
261
+ # if something looks like a bash conditional statement or loop,
262
+ # we'll avoid indenting the relevant keyword lines,
263
+ # but add indentation back for other lines.
264
+ # This is somewhat naive and won't handle multiple-level
265
+ # complex statements correctly,
266
+ # but is better than nothing.
239
267
  def folded_continuation_lines(first_line, related)
268
+ keywords = %w[case do done elif else esac fi for if then while]
269
+
240
270
  stripped = related.map(&:strip)
241
271
  stripped.unshift(first_line)
242
- stripped.join(" \\\n ")
272
+ stripped.inject do |accum, line|
273
+ if keywords.any? { |word| line =~ /^#{word} ?/ }
274
+ "#{accum} \\\n#{line}"
275
+ else
276
+ "#{accum} \\\n #{line}"
277
+ end
278
+ end
243
279
  end
244
280
 
245
281
  # While parsing multiline values,
@@ -248,6 +284,7 @@ module Litbuild
248
284
  # this is typically used for `file` directives,
249
285
  # which often appear in multiple directive blocks
250
286
  # and should be treated as a whole.
287
+ #
251
288
  # The indentation for multi-line values must be removed *later*,
252
289
  # during rendering of scripts and documents.
253
290
  def multiline_value(lines, indentation)
@@ -52,6 +52,7 @@ module Litbuild
52
52
  bp = library.blueprint_for(target: target)
53
53
  bp.accept(visitor: adv)
54
54
  adv.write_toplevel_doc(blueprint: bp)
55
+ $stderr.puts(adv.warning_admonitions) if adv.warning_admonitions
55
56
  end
56
57
 
57
58
  private
@@ -16,13 +16,17 @@ module Litbuild
16
16
  # slow, and recursive search is not useful to me, so I'm just
17
17
  # skipping it.
18
18
  class SourceCodeManager
19
- def initialize(*dirs)
20
- all_pkgfiles = dirs.map do |d|
21
- Dir.glob("#{d}/*.tar*") +
22
- Dir.glob("#{d}/*/*.tar*") +
23
- Dir.glob("#{d}/*.patch*") +
24
- Dir.glob("#{d}/*/*.patch*")
25
- end.flatten
19
+ # there are a variety of common compression programs that might be
20
+ # used; the file extension can be used to figure out which program
21
+ # to use.
22
+ DECOMPRESSORS = { 'gz' => 'gzip', 'bz2' => 'bzip2', 'lzma' => 'lzma',
23
+ 'lz' => 'lzip', 'xz' => 'xz' }.freeze
24
+
25
+ def initialize(dir)
26
+ all_pkgfiles = (Dir.glob("#{dir}/*.tar*") +
27
+ Dir.glob("#{dir}/*/*.tar*") +
28
+ Dir.glob("#{dir}/*.patch*") +
29
+ Dir.glob("#{dir}/*/*.patch*")).flatten
26
30
  abs_paths = all_pkgfiles.map { |f| File.expand_path(f) }
27
31
  @available_files = abs_paths.sort.uniq
28
32
  end
@@ -56,13 +60,13 @@ module Litbuild
56
60
  end
57
61
 
58
62
  ##
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.
63
+ # Package Users expect to have tarfiles for the top-level package
64
+ # and any in-tree packages in their `src` directory, and patches in
65
+ # their `patches` directory. This method arranges things that way:
66
+ # it produces commands that copy all the necessary files from
67
+ # MATERIALS_DIR to the destination directory, skipping any that are
68
+ # already present where the Package Users build script expects to
69
+ # find them.
66
70
  #
67
71
  # Binary package files are an exception to the typical case. A
68
72
  # binary package file has name `binary-#{packagename}.tar.lz` (no
@@ -117,7 +121,7 @@ module Litbuild
117
121
  end
118
122
 
119
123
  def find_file(filename)
120
- @available_files.detect { |f| /#{filename}/ =~ f } ||
124
+ @available_files.detect { |f| %r{/#{filename}} =~ f } ||
121
125
  raise(Litbuild::MissingSource,
122
126
  "File #{filename} is needed but not available.")
123
127
  end
@@ -133,9 +137,6 @@ module Litbuild
133
137
  "tar -x #{decompress_opt} -f #{tarfile}"
134
138
  end
135
139
 
136
- DECOMPRESSORS = { 'gz' => 'gzip', 'bz2' => 'bzip2', 'lzma' => 'lzma',
137
- 'lz' => 'lzip', 'xz' => 'xz' }.freeze
138
-
139
140
  def decompressor_for_file(file)
140
141
  ext = DECOMPRESSORS.keys.find { |e| file =~ /#{e}$/ }
141
142
  ext ? DECOMPRESSORS[ext] : nil
@@ -2,30 +2,26 @@
2
2
 
3
3
  module Litbuild
4
4
  module StringIndentation
5
- # Utility method to find the amount of blank space at the beginning
6
- # of a line.
5
+ # Find the amount of blank space at the beginning of a line.
7
6
  def indent_for(line)
8
7
  /^([[:blank:]]*).*/.match(line)[1].size
9
8
  end
10
9
 
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.
10
+ # Strip the common indentation from all strings in an array.
11
+ # Returns both the stripped strings and the amount of indentation removed.
14
12
  def strip_indentation_from_array(strings)
15
13
  common_indent = strings.map { |l| indent_for(l) }.min
16
14
  [strings.map { |l| l.slice(common_indent..-1) }, common_indent]
17
15
  end
18
16
 
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.)
17
+ # Strip the common indentation from all lines of a multi-line string.
18
+ # (This one does _not_ return the amount of indentation removed.)
22
19
  def strip_indentation_from_string(string)
23
20
  strip_indentation_from_array(string.lines)[0]
24
21
  end
25
22
 
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)
23
+ # Strip the common indentation from all lines of a directive value
24
+ # (which is expected to be an array containing a single string element)
29
25
  def strip_indentation_from_value(value)
30
26
  strip_indentation_from_string(value[0])
31
27
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Litbuild
4
- VERSION = '1.0.21'
4
+ VERSION = '1.0.27'
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: litbuild
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.21
4
+ version: 1.0.27
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Neumeier
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-08-16 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: A build system based on Knuth's idea of literate programming.
14
13
  email:
@@ -48,7 +47,6 @@ licenses:
48
47
  - GPL-3.0-only
49
48
  metadata:
50
49
  rubygems_mfa_required: 'true'
51
- post_install_message:
52
50
  rdoc_options: []
53
51
  require_paths:
54
52
  - lib
@@ -63,8 +61,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
61
  - !ruby/object:Gem::Version
64
62
  version: '0'
65
63
  requirements: []
66
- rubygems_version: 3.5.17
67
- signing_key:
64
+ rubygems_version: 3.6.9
68
65
  specification_version: 4
69
66
  summary: A literate build system
70
67
  test_files: []