litbuild 1.0.1
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 +7 -0
- checksums.yaml.gz.sig +1 -0
- data/LICENSE +14 -0
- data/README +135 -0
- data/bin/lb +69 -0
- data/gpl-3.0.txt +674 -0
- data/lib/litbuild/ascii_doc_visitor.rb +557 -0
- data/lib/litbuild/bash_script_visitor.rb +547 -0
- data/lib/litbuild/blueprint.rb +232 -0
- data/lib/litbuild/blueprint_library.rb +122 -0
- data/lib/litbuild/blueprint_parser.rb +210 -0
- data/lib/litbuild/commands.rb +37 -0
- data/lib/litbuild/driver.rb +66 -0
- data/lib/litbuild/errors.rb +39 -0
- data/lib/litbuild/logfile_namer.rb +23 -0
- data/lib/litbuild/multi_part_visitor.rb +35 -0
- data/lib/litbuild/narrative.rb +16 -0
- data/lib/litbuild/package.rb +143 -0
- data/lib/litbuild/section.rb +73 -0
- data/lib/litbuild/service_dir.rb +59 -0
- data/lib/litbuild/source_code_manager.rb +143 -0
- data/lib/litbuild/source_files_visitor.rb +26 -0
- data/lib/litbuild/url_visitor.rb +29 -0
- data/lib/litbuild/version.rb +5 -0
- data/lib/litbuild/visitor.rb +42 -0
- data/lib/litbuild.rb +16 -0
- data.tar.gz.sig +0 -0
- metadata +94 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Litbuild
|
4
|
+
class LogfileNamer
|
5
|
+
def initialize(log_dir)
|
6
|
+
@log_dir = log_dir
|
7
|
+
@counter = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def path_for(blueprint, phase = nil, stage = nil)
|
11
|
+
count = format('%03d', @counter)
|
12
|
+
@counter += 1
|
13
|
+
file_name = build_name(count, blueprint, phase, stage)
|
14
|
+
File.join(@log_dir, file_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def build_name(*elements)
|
20
|
+
elements.uniq.compact.join('-') + '.log'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'litbuild/errors'
|
4
|
+
require 'litbuild/visitor'
|
5
|
+
|
6
|
+
module Litbuild
|
7
|
+
##
|
8
|
+
# This is a base class for Visitors that can be sent to multiple Parts
|
9
|
+
# and/or Appendices. It is appropriate for Visitors like
|
10
|
+
# AsciiDocVisitors.
|
11
|
+
class MultiPartVisitor < Visitor
|
12
|
+
def initialize(directory:)
|
13
|
+
super
|
14
|
+
@other_parts = []
|
15
|
+
@appendices = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def visit_part(part)
|
19
|
+
@other_parts << part.name
|
20
|
+
part.accept(visitor: self)
|
21
|
+
end
|
22
|
+
|
23
|
+
# We need to call some methods on the appendix blueprint, so we
|
24
|
+
# store the entire appendix rather than just the name.
|
25
|
+
def visit_appendix(appendix)
|
26
|
+
@appendices << appendix
|
27
|
+
appendix.accept(visitor: self)
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
attr_reader :other_parts
|
33
|
+
attr_reader :appendices
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'litbuild/blueprint'
|
4
|
+
|
5
|
+
module Litbuild
|
6
|
+
class Narrative < Blueprint
|
7
|
+
def self.directory_name
|
8
|
+
'narratives'
|
9
|
+
end
|
10
|
+
|
11
|
+
def accept(visitor:)
|
12
|
+
super
|
13
|
+
visitor.visit_narrative(narrative: self)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'litbuild/blueprint'
|
4
|
+
|
5
|
+
module Litbuild
|
6
|
+
class Package < Blueprint
|
7
|
+
BUILD_STAGES = %w[configure compile test install].freeze
|
8
|
+
STAGE_DIRECTIVES = BUILD_STAGES.map { |stg| "#{stg}-commands" }.freeze
|
9
|
+
STAGE_DEFAULTS = { 'configure-commands' => './configure --prefix=/usr',
|
10
|
+
'compile-commands' => 'make',
|
11
|
+
'test-commands' => 'make check',
|
12
|
+
'install-commands' => 'make install' }.freeze
|
13
|
+
NONE = '(none)'
|
14
|
+
|
15
|
+
def self.directory_name
|
16
|
+
'packages'
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(text:)
|
20
|
+
super
|
21
|
+
if phases?
|
22
|
+
phases.each do |p|
|
23
|
+
synthesize_default_commands(@phase_directives[p], @phase_grafs[p])
|
24
|
+
end
|
25
|
+
else
|
26
|
+
synthesize_default_commands(@base_directives, @base_grafs)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def accept(visitor:)
|
31
|
+
super
|
32
|
+
visitor.visit_package(package: self)
|
33
|
+
end
|
34
|
+
|
35
|
+
def version
|
36
|
+
self['version'].first
|
37
|
+
end
|
38
|
+
|
39
|
+
def tar_files_needed
|
40
|
+
["#{name_and_version}.tar"] + in_tree_packages
|
41
|
+
end
|
42
|
+
|
43
|
+
def in_tree
|
44
|
+
in_tree_files = self['in-tree-sources'] || []
|
45
|
+
in_tree_files.map(&:split)
|
46
|
+
end
|
47
|
+
|
48
|
+
def in_tree_packages
|
49
|
+
in_tree.map { |name, version, _path| "#{name}-#{version}.tar" }
|
50
|
+
end
|
51
|
+
|
52
|
+
def patch_files
|
53
|
+
patches = self['patches'] || []
|
54
|
+
patches.map { |basename| "#{name_and_version}-#{basename}.patch" }
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_dir
|
58
|
+
value('build-dir')
|
59
|
+
end
|
60
|
+
|
61
|
+
def name_and_version
|
62
|
+
"#{name}-#{version}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_commands(stage)
|
66
|
+
commands = directives["#{stage}-commands"]
|
67
|
+
return [] if commands == [NONE]
|
68
|
+
|
69
|
+
commands
|
70
|
+
end
|
71
|
+
|
72
|
+
def pkgusr_name
|
73
|
+
value('package-user')['name'].first
|
74
|
+
end
|
75
|
+
|
76
|
+
def header_text
|
77
|
+
name
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Return the URL directive of the specified type, or `(unknown)` if
|
82
|
+
# there is none.
|
83
|
+
def url(url_type)
|
84
|
+
directives["#{url_type}-url"]&.first || '(unknown)'
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# This is called from the constructor, be careful if modifying it.
|
90
|
+
def synthesize_default_commands(directives, grafs)
|
91
|
+
to_add = {}
|
92
|
+
STAGE_DIRECTIVES.each do |sdir|
|
93
|
+
unless directives.include?(sdir)
|
94
|
+
directives[sdir] = [STAGE_DEFAULTS[sdir]]
|
95
|
+
to_add[sdir] = [STAGE_DEFAULTS[sdir]]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
add_to_narrative(grafs, to_add) unless to_add.empty?
|
99
|
+
end
|
100
|
+
|
101
|
+
# We have default commands to add to the narrative. Where do they
|
102
|
+
# go?
|
103
|
+
#
|
104
|
+
# The logic here is a little thorny (see comments embeddeed in this
|
105
|
+
# method for a description). For any occasion where it does not have
|
106
|
+
# a pleasing result, the blueprint can simply be written to have
|
107
|
+
# explicit directives rather than using defaults.
|
108
|
+
def add_to_narrative(grafs, to_add)
|
109
|
+
# *install* always goes at the end.
|
110
|
+
stages_to_consider = STAGE_DIRECTIVES.reverse
|
111
|
+
stg = stages_to_consider.shift
|
112
|
+
grafs << { stg => to_add[stg] } if to_add.key?(stg)
|
113
|
+
|
114
|
+
# For other stages...
|
115
|
+
until stages_to_consider.empty?
|
116
|
+
stg = stages_to_consider.shift
|
117
|
+
next unless to_add.key?(stg)
|
118
|
+
|
119
|
+
# if there is a previous stage *and* directives for that stage
|
120
|
+
# are present in the narrative, the default version goes right
|
121
|
+
# *after* the *last* of those directives;
|
122
|
+
previous_stage = stages_to_consider[0]
|
123
|
+
if previous_stage
|
124
|
+
last_idx = grafs.rindex do |graf|
|
125
|
+
graf.respond_to?(:key) && graf.key?(previous_stage)
|
126
|
+
end
|
127
|
+
if last_idx
|
128
|
+
grafs.insert(last_idx + 1, stg => to_add[stg])
|
129
|
+
next
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# otherwise, the default version goes right *before* the *first
|
134
|
+
# directive of the next stage* (which will always be present).
|
135
|
+
next_stage = STAGE_DIRECTIVES[STAGE_DIRECTIVES.index(stg) + 1]
|
136
|
+
first_idx = grafs.index do |graf|
|
137
|
+
graf.respond_to?(:key) && graf.key?(next_stage)
|
138
|
+
end
|
139
|
+
grafs.insert(first_idx, stg => to_add[stg])
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'litbuild/blueprint'
|
4
|
+
require 'litbuild/errors'
|
5
|
+
require 'litbuild/multi_part_visitor'
|
6
|
+
|
7
|
+
module Litbuild
|
8
|
+
class Section < Blueprint
|
9
|
+
def self.directory_name
|
10
|
+
'sections'
|
11
|
+
end
|
12
|
+
|
13
|
+
def accept(visitor:)
|
14
|
+
super
|
15
|
+
visitor.in_subdirectory(name) do
|
16
|
+
components.each do |bp|
|
17
|
+
bp.accept(visitor: visitor)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
visitor.visit_section(section: self)
|
21
|
+
return unless visitor.is_a?(MultiPartVisitor)
|
22
|
+
|
23
|
+
self['other-parts']&.each do |part|
|
24
|
+
part_bp = @bp_library.blueprint_for(target: part)
|
25
|
+
visitor.visit_part(part_bp)
|
26
|
+
end
|
27
|
+
self['appendices']&.each do |app|
|
28
|
+
app_bp = @bp_library.blueprint_for(target: app)
|
29
|
+
visitor.visit_appendix(app_bp)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def components
|
34
|
+
return @components if @components
|
35
|
+
|
36
|
+
blueprints = self['blueprints'].clone || []
|
37
|
+
if $DEBUG
|
38
|
+
warn("Explicit blueprints for section #{name}:" \
|
39
|
+
" #{blueprints.join(', ')}")
|
40
|
+
end
|
41
|
+
blueprints << automatic_inclusions
|
42
|
+
deduped = blueprints.flatten.uniq
|
43
|
+
@components = deduped.map do |bp|
|
44
|
+
@bp_library.blueprint_for(target: bp)
|
45
|
+
end
|
46
|
+
@components
|
47
|
+
end
|
48
|
+
|
49
|
+
def automatic_inclusions
|
50
|
+
auto_adds = []
|
51
|
+
@bp_library.blueprints.each_value do |bp|
|
52
|
+
if bp.phases.include?(name)
|
53
|
+
auto_adds << "#{bp.name}::#{name}"
|
54
|
+
elsif bp['in-section']&.include?(name)
|
55
|
+
auto_adds << bp.name
|
56
|
+
end
|
57
|
+
end
|
58
|
+
if $DEBUG
|
59
|
+
warn("Automatically-added blueprints for section #{name}:" \
|
60
|
+
" #{auto_adds.sort.join(', ')}")
|
61
|
+
end
|
62
|
+
auto_adds.sort
|
63
|
+
end
|
64
|
+
|
65
|
+
def success_line
|
66
|
+
'HUGE SUCCESS'
|
67
|
+
end
|
68
|
+
|
69
|
+
def failure_line
|
70
|
+
'DISMAL FAILURE'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Litbuild
|
4
|
+
# Service definition directories are a bit complicated. This is just
|
5
|
+
# a helper class that knows how to deal with them.
|
6
|
+
class ServiceDir
|
7
|
+
def initialize(svcdef)
|
8
|
+
@svcdef = svcdef
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
@svcdef['name'].first
|
13
|
+
end
|
14
|
+
|
15
|
+
def bundle
|
16
|
+
@svcdef['bundle']&.first
|
17
|
+
end
|
18
|
+
|
19
|
+
def type
|
20
|
+
return @svcdef['type'].first if @svcdef.key?('type')
|
21
|
+
return 'longrun' if @svcdef.key?('run')
|
22
|
+
return 'oneshot' if @svcdef.key?('up')
|
23
|
+
|
24
|
+
raise(InvalidDirective, "servicedir #{name} must specify type")
|
25
|
+
end
|
26
|
+
|
27
|
+
def oneline_files
|
28
|
+
file_contents = { 'type' => type }
|
29
|
+
%w[down-signal notification-fd].each do |fn|
|
30
|
+
file_contents[fn] = @svcdef[fn].first if @svcdef.key?(fn)
|
31
|
+
end
|
32
|
+
file_contents
|
33
|
+
end
|
34
|
+
|
35
|
+
def dependencies
|
36
|
+
@svcdef['dependencies'] || []
|
37
|
+
end
|
38
|
+
|
39
|
+
def multiline_files
|
40
|
+
file_contents = {}
|
41
|
+
%w[up down run].each do |fn|
|
42
|
+
next unless @svcdef.key?(fn)
|
43
|
+
|
44
|
+
file_contents[fn] = @svcdef[fn]
|
45
|
+
end
|
46
|
+
file_contents
|
47
|
+
end
|
48
|
+
|
49
|
+
def env
|
50
|
+
return [] unless @svcdef.key?('env')
|
51
|
+
|
52
|
+
flattened = {}
|
53
|
+
@svcdef['env'].first.each do |variable, value|
|
54
|
+
flattened[variable] = value.first
|
55
|
+
end
|
56
|
+
flattened
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'litbuild/errors'
|
4
|
+
|
5
|
+
module Litbuild
|
6
|
+
##
|
7
|
+
# Find all of the tar files and patch files, possibly compressed,
|
8
|
+
# present in any of the directories passed to the constructor or any
|
9
|
+
# direct subdirectories of those directories. Given a package
|
10
|
+
# blueprint, provide suitable commands to unpack tar files and in-tree
|
11
|
+
# source tarfiles, apply patch files, or copy those files to a package
|
12
|
+
# user home directory.
|
13
|
+
#
|
14
|
+
# Note, SourceCodeManager does not recurse into additional levels of
|
15
|
+
# subdirectories -- it turns out that makes the test suite really
|
16
|
+
# slow.
|
17
|
+
class SourceCodeManager
|
18
|
+
def initialize(*dirs)
|
19
|
+
all_pkgfiles = dirs.map do |d|
|
20
|
+
Dir.glob("#{d}/*.tar*") +
|
21
|
+
Dir.glob("#{d}/*/*.tar*") +
|
22
|
+
Dir.glob("#{d}/*.patch*") +
|
23
|
+
Dir.glob("#{d}/*/*.patch*")
|
24
|
+
end.flatten
|
25
|
+
abs_paths = all_pkgfiles.map { |f| File.expand_path(f) }
|
26
|
+
@available_files = abs_paths.sort.uniq
|
27
|
+
end
|
28
|
+
|
29
|
+
def untar_command_for(package)
|
30
|
+
unpack_tar(package.name_and_version)
|
31
|
+
end
|
32
|
+
|
33
|
+
def intree_untar_commands_for(package)
|
34
|
+
commands = []
|
35
|
+
package.in_tree.each do |basename, version, path|
|
36
|
+
intree = "#{basename}-#{version}"
|
37
|
+
commands << unpack_tar(intree)
|
38
|
+
if path
|
39
|
+
commands << "mkdir -p #{File.dirname(path)}"
|
40
|
+
commands << "mv #{intree} #{path}"
|
41
|
+
else
|
42
|
+
commands << "mv #{intree} #{basename}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
commands
|
46
|
+
end
|
47
|
+
|
48
|
+
def patch_commands_for(package)
|
49
|
+
return [] unless package.patch_files
|
50
|
+
|
51
|
+
package.patch_files.map do |patch_file|
|
52
|
+
full_fn = find_file(patch_file)
|
53
|
+
"#{decompress_command(full_fn)} < #{full_fn} | patch -p1"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
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)
|
64
|
+
pkgusr = pkgusr_name(package)
|
65
|
+
mkdir_commands = %w[src patches].map do |dir|
|
66
|
+
"mkdir -p ~#{pkgusr}/#{dir}"
|
67
|
+
end
|
68
|
+
copy_commands = []
|
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
|
73
|
+
end
|
74
|
+
package.patch_files.each do |filename|
|
75
|
+
unless pkgusr_file_available?(package, filename)
|
76
|
+
copy_commands << "cp #{find_file(filename)} ~#{pkgusr}/patches"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
mkdir_commands + copy_commands.sort
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def pkgusr_file_available?(package, filename)
|
85
|
+
pkghome = homedir(package)
|
86
|
+
return false unless pkghome
|
87
|
+
|
88
|
+
dir = filename.match?(/.tar$/) ? 'src' : 'patches'
|
89
|
+
possible_names = DECOMPRESSORS.keys.map { |ext| "#{filename}.#{ext}" }
|
90
|
+
possible_names << filename
|
91
|
+
possible_names.any? { |f| File.exist?(File.join(pkghome, dir, f)) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def find_file(filename)
|
95
|
+
@available_files.detect { |f| /#{filename}/ =~ f } ||
|
96
|
+
raise(Litbuild::MissingSource,
|
97
|
+
"File #{filename} is needed but not available.")
|
98
|
+
end
|
99
|
+
|
100
|
+
def find_tarfile(filename)
|
101
|
+
filename = "#{filename}.tar" unless filename.end_with?('.tar')
|
102
|
+
find_file(filename)
|
103
|
+
end
|
104
|
+
|
105
|
+
def unpack_tar(basename)
|
106
|
+
tarfile = find_tarfile(basename)
|
107
|
+
decompress_opt = tar_decompress_opt(tarfile)
|
108
|
+
"tar -x #{decompress_opt} -f #{tarfile}"
|
109
|
+
end
|
110
|
+
|
111
|
+
DECOMPRESSORS = { 'gz' => 'gzip', 'bz2' => 'bzip2', 'lzma' => 'lzma',
|
112
|
+
'lz' => 'lzip', 'xz' => 'xz' }.freeze
|
113
|
+
|
114
|
+
def decompressor_for_file(file)
|
115
|
+
ext = DECOMPRESSORS.keys.find { |e| file =~ /#{e}$/ }
|
116
|
+
ext ? DECOMPRESSORS[ext] : nil
|
117
|
+
end
|
118
|
+
|
119
|
+
def decompress_command(filename)
|
120
|
+
decompressor = decompressor_for_file(filename)
|
121
|
+
decompressor ? "#{decompressor} -d" : 'cat'
|
122
|
+
end
|
123
|
+
|
124
|
+
def tar_decompress_opt(tarfile)
|
125
|
+
decompressor = decompressor_for_file(tarfile)
|
126
|
+
decompressor ? "--use-compress-program=#{decompressor}" : ''
|
127
|
+
end
|
128
|
+
|
129
|
+
def pkgusr_name(package)
|
130
|
+
pkgusr = package['package-user'].first
|
131
|
+
name = pkgusr['name'].first
|
132
|
+
raise(InvalidDirective, 'package-user missing name') unless name
|
133
|
+
|
134
|
+
name
|
135
|
+
end
|
136
|
+
|
137
|
+
def homedir(package)
|
138
|
+
Dir.home(pkgusr_name(package))
|
139
|
+
rescue ArgumentError
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require 'litbuild/visitor'
|
4
|
+
|
5
|
+
module Litbuild
|
6
|
+
# This is a simple Visitor that just accumulates the files needed to
|
7
|
+
# build a set of Pacakge blueprints.
|
8
|
+
class SourceFilesVisitor < Visitor
|
9
|
+
def initialize
|
10
|
+
super
|
11
|
+
@files = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def visit_package(package:)
|
15
|
+
@files << files_needed(package)
|
16
|
+
end
|
17
|
+
|
18
|
+
def files_needed(pkg)
|
19
|
+
["#{pkg.name_and_version}.tar"] + pkg.patch_files + pkg.in_tree_packages
|
20
|
+
end
|
21
|
+
|
22
|
+
def files
|
23
|
+
@files.compact.flatten.sort.uniq
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require 'litbuild/visitor'
|
4
|
+
|
5
|
+
module Litbuild
|
6
|
+
# This is a simple Visitor that just accumulates the best download URL
|
7
|
+
# for a set of Package blueprints.
|
8
|
+
class UrlVisitor < Visitor
|
9
|
+
def initialize
|
10
|
+
super
|
11
|
+
@urls = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def visit_package(package:)
|
15
|
+
@urls << download_url(package)
|
16
|
+
end
|
17
|
+
|
18
|
+
def download_url(pkg)
|
19
|
+
url = pkg.directives['download-url'] ||
|
20
|
+
pkg.directives['project-url'] ||
|
21
|
+
pkg.directives['scm-url']
|
22
|
+
url ? url.first : "#{name}: no download URL known"
|
23
|
+
end
|
24
|
+
|
25
|
+
def urls
|
26
|
+
@urls.compact.flatten.sort.uniq
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module Litbuild
|
4
|
+
##
|
5
|
+
# This is the base class for Litubild Visitors; it defines a
|
6
|
+
# no-operation Visitor. Each Visitor subclass encapsulates one of the
|
7
|
+
# operations that can be performed on a Blueprint along with its
|
8
|
+
# dependencies and components (and along with the dependencies and
|
9
|
+
# components of those Blueprints and so on).
|
10
|
+
class Visitor
|
11
|
+
# The main Visitor classes (bash script, asciidoc) write files to
|
12
|
+
# specific directories, and need to be able to switch to
|
13
|
+
# subdirectories tmeporarily; so Litbuild::Visitor has some
|
14
|
+
# functionality to support that.
|
15
|
+
#
|
16
|
+
# The default '/' value is just to avoid nil checking for Visitors
|
17
|
+
# that don't need this feature.
|
18
|
+
def initialize(directory: '/')
|
19
|
+
@dir_stack = [directory]
|
20
|
+
end
|
21
|
+
|
22
|
+
def in_subdirectory(subdir)
|
23
|
+
@dir_stack.push(File.join(cwd, subdir))
|
24
|
+
yield
|
25
|
+
@dir_stack.pop
|
26
|
+
end
|
27
|
+
|
28
|
+
def visit_commands(commands:); end
|
29
|
+
|
30
|
+
def visit_narrative(narrative:); end
|
31
|
+
|
32
|
+
def visit_package(package:); end
|
33
|
+
|
34
|
+
def visit_section(section:); end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def cwd
|
39
|
+
@dir_stack.last
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/litbuild.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'litbuild/blueprint'
|
4
|
+
require 'litbuild/blueprint_library'
|
5
|
+
require 'litbuild/blueprint_parser'
|
6
|
+
require 'litbuild/commands'
|
7
|
+
require 'litbuild/driver'
|
8
|
+
require 'litbuild/errors'
|
9
|
+
require 'litbuild/logfile_namer'
|
10
|
+
require 'litbuild/narrative'
|
11
|
+
require 'litbuild/package'
|
12
|
+
require 'litbuild/section'
|
13
|
+
require 'litbuild/source_files_visitor'
|
14
|
+
require 'litbuild/url_visitor'
|
15
|
+
require 'litbuild/version'
|
16
|
+
require 'litbuild/visitor'
|
data.tar.gz.sig
ADDED
Binary file
|