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 +4 -4
- data/README +87 -62
- data/lib/litbuild/ascii_doc_visitor.rb +28 -3
- data/lib/litbuild/bash_script_visitor.rb +31 -27
- data/lib/litbuild/blueprint_library.rb +10 -4
- data/lib/litbuild/blueprint_parser.rb +50 -13
- data/lib/litbuild/driver.rb +1 -0
- data/lib/litbuild/source_code_manager.rb +19 -18
- data/lib/litbuild/string_indentation.rb +7 -11
- data/lib/litbuild/version.rb +1 -1
- metadata +3 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cce41178693bb69ed91330ddebbedeb7db27b71263c52e9841010e328e2e9b05
|
4
|
+
data.tar.gz: 05de3849a236d875dd817b829f37fc461f177fa38860002a39a9e3f8f6db3845
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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)
|
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
|
13
|
-
|
14
|
-
program works and what the purpose
|
15
|
-
declaration, subroutine or coroutine, or
|
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
|
20
|
-
the project codebase _per se,_ and let the build system
|
21
|
-
to compile and test it. There are lots of build tools,
|
22
|
-
variety of design goals and value systems. Regardless
|
23
|
-
used, though, I have noticed that once a software
|
24
|
-
certain level of complexity, the
|
25
|
-
complex -- to the point that it is a major undertaking to
|
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
|
30
|
-
incomprehensible
|
31
|
-
|
32
|
-
working for some reason,
|
33
|
-
on a team or project to
|
34
|
-
|
35
|
-
|
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.
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
54
|
+
document source (which you can process using a typesetting system like
|
52
55
|
TeX or Docbook).
|
53
56
|
|
54
|
-
Litbuild is
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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.
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
you want to build
|
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
|
92
|
-
default values for all these parameters will be provided
|
93
|
-
|
94
|
-
|
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
|
106
|
-
blueprints are needed to resolve those dependencies. If all
|
107
|
-
are correctly specified in all blueprints, all you really
|
108
|
-
request the final target! But it's still a good idea to
|
109
|
-
for any situation where the various components of a
|
110
|
-
have an overall framing narrative or discussion to
|
111
|
-
comprehensible, or when all the parts of a build need to share
|
112
|
-
configuration or environment -- which is the case for most
|
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
|
-
|
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
|
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
|
-
|
146
|
-
eliminate its dependency on ruby; if
|
147
|
-
|
148
|
-
|
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
|
197
|
-
|
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
|
-
|
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['
|
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('
|
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('
|
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: $
|
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(
|
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, '
|
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
|
-
"
|
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,
|
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
|
639
|
-
# if the value contains
|
640
|
-
# if the value contains
|
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
|
18
|
-
SCRIPT_DIR
|
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
|
107
|
-
#
|
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
|
-
#
|
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,
|
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
|
66
|
-
#
|
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
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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.
|
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)
|
data/lib/litbuild/driver.rb
CHANGED
@@ -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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
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}
|
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
|
-
#
|
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
|
-
#
|
12
|
-
#
|
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
|
-
#
|
20
|
-
#
|
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
|
-
#
|
27
|
-
#
|
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
|
data/lib/litbuild/version.rb
CHANGED
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.
|
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:
|
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.
|
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: []
|