dgd-tools 0.1.2 → 0.1.7
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/.gitignore +2 -0
- data/Gemfile.lock +7 -1
- data/README.md +14 -0
- data/bin/console +1 -0
- data/dgd-tools.gemspec +3 -0
- data/example_xml/CraftDaemon.xml +42 -0
- data/example_xml/Materials.xml +3 -0
- data/example_xml/PropertyTypes.xml +49 -0
- data/example_xml/Thing.xml +41 -0
- data/example_xml/Thing2.xml +42 -0
- data/example_xml/t1/Thing.xml +41 -0
- data/example_xml/t2/Thing.xml +42 -0
- data/exe/dgd-manifest +40 -1
- data/exe/skotos-xml-diff +45 -0
- data/goods/chattheatre_kernellib.goods +12 -0
- data/lib/dgd-tools/manifest.rb +375 -88
- data/lib/dgd-tools/skotos_xml_obj.rb +283 -0
- data/lib/dgd-tools/version.rb +1 -1
- metadata +42 -3
data/lib/dgd-tools/manifest.rb
CHANGED
@@ -16,7 +16,6 @@ module DGD::Manifest
|
|
16
16
|
}
|
17
17
|
KERNEL_PATHS = KERNEL_PATH_MAP.values
|
18
18
|
DEFAULT_KERNELLIB_URL = "https://github.com/ChatTheatre/kernellib"
|
19
|
-
|
20
19
|
GENERATED_ROOT = ".root"
|
21
20
|
|
22
21
|
def self.system_call(cmd)
|
@@ -30,24 +29,26 @@ module DGD::Manifest
|
|
30
29
|
# This is a repo of everything DGD Manifest saves between runs.
|
31
30
|
# It includes downloaded Git repos, Goods files and more.
|
32
31
|
class Repo
|
33
|
-
attr_reader :
|
32
|
+
attr_reader :shared_dir
|
34
33
|
|
35
34
|
def initialize
|
35
|
+
@no_manifest_file = true
|
36
36
|
@home = ENV["HOME"]
|
37
|
-
@
|
38
|
-
Dir.mkdir(@
|
37
|
+
@shared_dir = "#{@home}/.dgd-tools"
|
38
|
+
Dir.mkdir(@shared_dir) unless File.directory?(@shared_dir)
|
39
|
+
|
39
40
|
["git", "goods"].each do |subdir|
|
40
|
-
full_subdir = "#{@
|
41
|
+
full_subdir = "#{@shared_dir}/#{subdir}"
|
41
42
|
Dir.mkdir(full_subdir) unless File.directory?(full_subdir)
|
42
43
|
end
|
43
44
|
|
44
|
-
unless File.exist?("#{@
|
45
|
-
dgd_dir = "#{@
|
45
|
+
unless File.exist?("#{@shared_dir}/dgd/bin/dgd")
|
46
|
+
dgd_dir = "#{@shared_dir}/dgd"
|
46
47
|
if File.directory?(dgd_dir)
|
47
48
|
# Not clear to me what to do here...
|
48
49
|
else
|
49
50
|
DGD::Manifest.system_call("git clone https://github.com/ChatTheatre/dgd.git #{dgd_dir}")
|
50
|
-
Dir.chdir("#{@
|
51
|
+
Dir.chdir("#{@shared_dir}/dgd/src") do
|
51
52
|
DGD::Manifest.system_call(DGD_BUILD_COMMAND)
|
52
53
|
end
|
53
54
|
end
|
@@ -60,77 +61,120 @@ module DGD::Manifest
|
|
60
61
|
end
|
61
62
|
|
62
63
|
def manifest_file(path)
|
63
|
-
raise "Already have a dgd.manifest file!"
|
64
|
+
raise "Already have a dgd.manifest file!" unless @no_manifest_file
|
64
65
|
|
66
|
+
@no_manifest_file = false
|
65
67
|
@manifest_file ||= AppFile.new(self, path)
|
66
68
|
end
|
67
69
|
|
68
|
-
|
69
|
-
dgd_root = "#{File.expand_path(location)}/#{GENERATED_ROOT}"
|
70
|
-
app_path = "#{File.expand_path(location)}/#{@manifest_file.app_root}"
|
71
|
-
FileUtils.rm_rf(dgd_root)
|
72
|
-
FileUtils.cp_r(app_path, dgd_root)
|
70
|
+
protected
|
73
71
|
|
74
|
-
|
75
|
-
|
72
|
+
# This includes files to assemble... But also subdirectories and commands. This format is
|
73
|
+
# unstable and ugly, and should not be exposed to outside parties who might later depend on it.
|
74
|
+
def assembly_operations(location)
|
75
|
+
operations = []
|
76
76
|
|
77
|
-
|
77
|
+
raise("No manifest file!") if @no_manifest_file
|
78
78
|
|
79
|
-
specs.each do |spec|
|
80
|
-
|
81
|
-
|
79
|
+
@manifest_file.specs.each do |spec|
|
80
|
+
spec_git_repo = spec.source
|
81
|
+
spec_git_repo.use_details(spec.source_details) # This sets things like checked-out branch
|
82
82
|
|
83
83
|
spec.paths.each do |from, to|
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
# Note: spec_git_repo.local_dir is an absolute path.
|
85
|
+
from_path = "#{spec_git_repo.local_dir}/#{from}"
|
86
|
+
if File.directory?(from_path)
|
87
|
+
files = Dir["#{from_path}/**/*"].to_a + Dir["#{from_path}/**/.*"].to_a
|
88
|
+
dirs = files.select { |file| File.directory?(file) }
|
89
|
+
non_dirs = files - dirs
|
90
|
+
operations << { cmd: "cp", from: from_path, to: to, dirs: dirs, non_dirs: non_dirs, comment: :single_dir }
|
91
|
+
elsif from_path["*"] # If from_path contains at least one asterisk
|
92
|
+
components = from.split("/")
|
93
|
+
first_wild_idx = components.index { |item| item["*"] }
|
94
|
+
no_wild_from_path = components[0..(first_wild_idx-1)].join("/")
|
95
|
+
wild_path = components[first_wild_idx..-1].join("/")
|
96
|
+
|
97
|
+
files = Dir["#{spec_git_repo.local_dir}/#{no_wild_from_path}/#{wild_path}"].to_a
|
98
|
+
dirs = files.select { |file| File.directory?(file) }
|
99
|
+
dirs += files.map { |f| File.dirname(f) }
|
100
|
+
dirs.uniq!
|
101
|
+
|
102
|
+
non_dirs = files - dirs
|
103
|
+
operations << { cmd: "cp", from: "#{spec_git_repo.local_dir}/#{no_wild_from_path}", to: to, dirs: dirs, non_dirs: non_dirs, comment: :path_wildcard }
|
104
|
+
else
|
105
|
+
# A single file
|
106
|
+
operations << { cmd: "cp", from: from_path, to: to, dirs: [], non_dirs: [from_path], comment: :single_file }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
app_path = "#{File.expand_path(location)}/#{@manifest_file.app_root}"
|
112
|
+
app_files = Dir["#{app_path}/**/*"].to_a
|
113
|
+
app_dirs = app_files.select { |f| File.directory?(f) }
|
114
|
+
app_non_dirs = app_files - app_dirs
|
115
|
+
unless app_dirs.empty? && app_non_dirs.empty?
|
116
|
+
operations << { cmd: "cp", from: app_path, to: ".", dirs: app_dirs, non_dirs: app_non_dirs, comment: :app_files } # No source
|
117
|
+
end
|
118
|
+
|
119
|
+
operations
|
120
|
+
end
|
121
|
+
|
122
|
+
public
|
123
|
+
|
124
|
+
def dgd_root(location)
|
125
|
+
"#{File.expand_path(location)}/#{GENERATED_ROOT}"
|
126
|
+
end
|
127
|
+
|
128
|
+
def assemble_app(location)
|
129
|
+
Dir[File.join(dgd_root(location), "*")].each { |dir| FileUtils.rm_rf dir }
|
130
|
+
|
131
|
+
write_app_files(location)
|
132
|
+
end
|
133
|
+
|
134
|
+
def update_app(location)
|
135
|
+
write_app_files(location) # TODO: maybe check files dates? Some way to do update-only
|
136
|
+
end
|
137
|
+
|
138
|
+
protected
|
139
|
+
|
140
|
+
def write_app_files(location)
|
141
|
+
Dir.chdir(location) do
|
142
|
+
write_config_file("#{location}/dgd.config")
|
143
|
+
FileUtils.mkdir_p("#{location}/state") # Statedir for statedumps, editor files, etc.
|
144
|
+
|
145
|
+
assembly_operations(location).each do |sd_hash|
|
146
|
+
to_path = "#{dgd_root(location)}/#{sd_hash[:to]}"
|
147
|
+
|
148
|
+
# Make appropriate dirs, including empty ones
|
149
|
+
sd_hash[:dirs].each do |dir|
|
150
|
+
FileUtils.mkdir_p dir.sub(sd_hash[:from], to_path)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Copy all files
|
154
|
+
sd_hash[:non_dirs].each do |from_file|
|
155
|
+
to_file = from_file.sub(sd_hash[:from], "#{dgd_root(location)}/#{sd_hash[:to]}")
|
156
|
+
to_dir = File.dirname(to_file)
|
157
|
+
FileUtils.mkdir_p to_dir
|
158
|
+
FileUtils.cp from_file, to_file
|
159
|
+
end
|
87
160
|
end
|
88
161
|
end
|
162
|
+
end
|
89
163
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
164
|
+
public
|
165
|
+
|
166
|
+
def precheck(location)
|
167
|
+
all_files = assembly_operations(location).flat_map { |sd| sd[:non_dirs] }
|
168
|
+
|
169
|
+
if all_files.size != all_files.uniq.size
|
170
|
+
repeated = all_files.uniq.select { |f| all_files.count(f) > 1 }
|
171
|
+
raise "Error in dgd.manifest! Repeated files: #{repeated.inspect} / #{all_files.inspect}"
|
95
172
|
end
|
96
173
|
end
|
97
174
|
|
98
175
|
def write_config_file(path)
|
99
176
|
File.open(path, "wb") do |f|
|
100
|
-
f.write
|
101
|
-
/* These are SkotOS limits. They are enormous. They should
|
102
|
-
be configurable but they are not yet. */
|
103
|
-
telnet_port = ([
|
104
|
-
"*":50100 /* telnet port number */
|
105
|
-
]);
|
106
|
-
binary_port = ([
|
107
|
-
"*":50110 /* Failsafe */
|
108
|
-
]); /* binary ports */
|
109
|
-
directory = "./#{GENERATED_ROOT}";
|
110
|
-
|
111
|
-
users = 100; /* max # of users */
|
112
|
-
editors = 40; /* max # of editor sessions */
|
113
|
-
ed_tmpfile = "../state/ed"; /* proto editor tmpfile */
|
114
|
-
swap_file = "../state/swap"; /* swap file */
|
115
|
-
swap_size = 1048576; /* # sectors in swap file */
|
116
|
-
sector_size = 512; /* swap sector size */
|
117
|
-
swap_fragment = 4096; /* fragment to swap out */
|
118
|
-
static_chunk = 64512; /* static memory chunk */
|
119
|
-
dynamic_chunk = 261120; /* dynamic memory chunk */
|
120
|
-
dump_file = "../state/dump"; /* dump file */
|
121
|
-
dump_interval = 3600; /* dump interval */
|
122
|
-
|
123
|
-
typechecking = 2; /* highest level of typechecking */
|
124
|
-
include_file = "/include/std.h"; /* standard include file */
|
125
|
-
include_dirs = ({ "/include", "~/include" }); /* directories to search */
|
126
|
-
auto_object = "/kernel/lib/auto"; /* auto inherited object */
|
127
|
-
driver_object = "/kernel/sys/driver"; /* driver object */
|
128
|
-
create = "_F_create"; /* name of create function */
|
129
|
-
|
130
|
-
array_size = 16384; /* max array size */
|
131
|
-
objects = 262144; /* max # of objects */
|
132
|
-
call_outs = 16384; /* max # of call_outs */
|
133
|
-
CONTENTS
|
177
|
+
f.write @manifest_file.dgd_config.as_file
|
134
178
|
end
|
135
179
|
end
|
136
180
|
end
|
@@ -145,7 +189,7 @@ CONTENTS
|
|
145
189
|
@git_url = git_url
|
146
190
|
@repo = repo
|
147
191
|
local_path = git_url.tr("/\\", "_")
|
148
|
-
@local_dir = "#{@repo.
|
192
|
+
@local_dir = "#{@repo.shared_dir}/git/#{local_path}"
|
149
193
|
|
150
194
|
if File.directory?(@local_dir)
|
151
195
|
Dir.chdir(@local_dir) do
|
@@ -157,19 +201,17 @@ CONTENTS
|
|
157
201
|
end
|
158
202
|
|
159
203
|
def default_branch
|
160
|
-
|
161
|
-
output = `git rev-parse --abbrev-ref origin/HEAD`.chomp
|
162
|
-
@default_branch = output.gsub(/^origin\//, "")
|
204
|
+
@default_branch ||= `git rev-parse --abbrev-ref origin/HEAD`.chomp.gsub(/^origin\//, "")
|
163
205
|
end
|
164
206
|
|
165
207
|
def use_details(details)
|
166
208
|
if details["branch"]
|
167
209
|
Dir.chdir(@local_dir) do
|
168
|
-
DGD::Manifest.system_call("git checkout #{details["branch"]}")
|
210
|
+
DGD::Manifest.system_call("git checkout #{details["branch"]} && git pull")
|
169
211
|
end
|
170
212
|
else
|
171
213
|
Dir.chdir(@local_dir) do
|
172
|
-
DGD::Manifest.system_call("git checkout #{default_branch}")
|
214
|
+
DGD::Manifest.system_call("git checkout #{default_branch} && git pull")
|
173
215
|
end
|
174
216
|
end
|
175
217
|
end
|
@@ -179,47 +221,64 @@ CONTENTS
|
|
179
221
|
attr_reader :path
|
180
222
|
attr_reader :repo
|
181
223
|
attr_reader :specs
|
182
|
-
attr_reader :
|
224
|
+
attr_reader :dgd_config
|
183
225
|
|
184
226
|
def initialize(repo, path)
|
185
227
|
@path = path
|
186
228
|
@repo = repo
|
187
229
|
raise("No such dgd.manifest file as #{path.inspect}!") unless File.exist?(path)
|
188
|
-
contents =
|
230
|
+
contents = AppFile.parse_manifest_file(path)
|
189
231
|
|
190
232
|
read_manifest_file(contents)
|
191
233
|
|
192
|
-
@app_root = contents["app_root"] || "app"
|
193
|
-
|
194
234
|
output_paths = @specs.flat_map { |s| s.paths.values }
|
195
235
|
unless output_paths == output_paths.uniq
|
196
236
|
repeated_paths = output_paths.select { |p| output_paths.count(p) > 1 }
|
197
237
|
raise "Repeated (conflicting?) paths in dgd.manifest! #{repeated_paths.inspect}"
|
198
238
|
end
|
199
239
|
|
200
|
-
|
201
|
-
if KERNEL_PATHS.any? { |kp| output_paths.include?(kp) }
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
else
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
end
|
240
|
+
## Make sure the dgd.manifest file overrides either no kernel paths or both/all
|
241
|
+
#if KERNEL_PATHS.any? { |kp| output_paths.include?(kp) }
|
242
|
+
# unless KERNEL_PATHS.all? { |kp| output_paths.include?(kp) }
|
243
|
+
# raise "dgd.manifest file #{path.inspect} includes some Kernel Library paths but not all! All needed: #{KERNEL_PATHS}!"
|
244
|
+
# end
|
245
|
+
# puts "This dgd.manifest file overrides the Kernel Library with its own."
|
246
|
+
#else
|
247
|
+
# puts "This dgd.manifest needs the default Kernel Library."
|
248
|
+
# # This app has specified no kernellib paths -- add them
|
249
|
+
# spec_git_repo = @repo.git_repo(DEFAULT_KERNELLIB_URL)
|
250
|
+
# klib_spec = GoodsSpec.new @repo, name: "default Kernel Library",
|
251
|
+
# source: spec_git_repo, paths: KERNEL_PATH_MAP
|
252
|
+
# specs.unshift klib_spec
|
253
|
+
#end
|
214
254
|
|
215
255
|
nil
|
216
256
|
end
|
217
257
|
|
258
|
+
# Load the JSON and then remove comments
|
259
|
+
def self.parse_manifest_file(path)
|
260
|
+
contents = JSON.parse(File.read path)
|
261
|
+
remove_comments!(contents)
|
262
|
+
contents
|
263
|
+
end
|
264
|
+
|
265
|
+
def self.remove_comments!(items)
|
266
|
+
if items.is_a?(Hash)
|
267
|
+
items.delete_if { |k, v| k[0] == "#" }
|
268
|
+
items.values.each { |v| remove_comments!(v) }
|
269
|
+
elsif items.is_a?(Array)
|
270
|
+
items.delete_if { |i| i.is_a?(String) && i[0] == "#" }
|
271
|
+
items.each { |i| remove_comments!(i) }
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
218
275
|
def read_manifest_file(contents)
|
219
276
|
raise "Expected a top-level JSON object in dgd.manifest!" unless contents.is_a?(Hash)
|
220
277
|
|
221
278
|
@specs = []
|
222
279
|
|
280
|
+
@dgd_config = DGDRuntimeConfig.new (contents["config"] || {})
|
281
|
+
|
223
282
|
if contents["unbundled_goods"]
|
224
283
|
raise "Unbundled_goods must be an array!" unless contents["unbundled_goods"].is_a?(Array)
|
225
284
|
|
@@ -241,9 +300,15 @@ CONTENTS
|
|
241
300
|
end
|
242
301
|
end
|
243
302
|
|
303
|
+
def app_root
|
304
|
+
@dgd_config.app_root
|
305
|
+
end
|
306
|
+
|
244
307
|
def unbundled_json_to_spec(fields)
|
245
308
|
source = nil
|
246
309
|
source_details = nil
|
310
|
+
dependencies = []
|
311
|
+
|
247
312
|
if fields["git"]
|
248
313
|
raise "A git source requires a git url: #{fields.inspect}!" unless fields["git"]["url"]
|
249
314
|
source = @repo.git_repo(fields["git"]["url"])
|
@@ -256,7 +321,23 @@ CONTENTS
|
|
256
321
|
raise "Paths in Goods files must map strings to strings! #{fields["paths"].inspect}"
|
257
322
|
end
|
258
323
|
|
259
|
-
|
324
|
+
if fields["dependencies"]
|
325
|
+
# For now, permit a single string as a dependency.
|
326
|
+
fields["dependencies"] = [ fields["dependencies"] ] if fields["dependencies"].is_a?(String)
|
327
|
+
|
328
|
+
fields["dependencies"].each do |dep|
|
329
|
+
if dep.is_a?(String)
|
330
|
+
dependencies.push "url" => dep
|
331
|
+
elsif dep.is_a?(Hash)
|
332
|
+
raise "Currently only URL-based dependencies on Goods files are supported!" unless dep["url"]
|
333
|
+
dependencies.push dep
|
334
|
+
else
|
335
|
+
raise "Unexpected dependency type #{dep.class} when parsing DGD Manifest specs, item: #{dep.inspect}"
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
spec = GoodsSpec.new(@repo, name: fields["name"], source: source, source_details: source_details, paths: fields["paths"], dependencies: dependencies)
|
260
341
|
return spec
|
261
342
|
end
|
262
343
|
end
|
@@ -267,8 +348,9 @@ CONTENTS
|
|
267
348
|
attr_reader :source
|
268
349
|
attr_reader :source_details
|
269
350
|
attr_reader :paths
|
351
|
+
attr_reader :dependencies
|
270
352
|
|
271
|
-
def initialize(repo, name:, source:, source_details: {}, paths:)
|
353
|
+
def initialize(repo, name:, source:, source_details: {}, paths:, dependencies:)
|
272
354
|
@repo = repo
|
273
355
|
@name = name
|
274
356
|
@source = source
|
@@ -281,6 +363,211 @@ CONTENTS
|
|
281
363
|
end
|
282
364
|
|
283
365
|
@paths = cleaned_paths
|
366
|
+
@dependencies = dependencies
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
class AppDirectory
|
371
|
+
attr_reader :location
|
372
|
+
attr_accessor :name
|
373
|
+
|
374
|
+
DEFAULT_FILE_LOCATIONS = {
|
375
|
+
"manifest" => "dgd.manifest",
|
376
|
+
"gitignore" => ".gitignore",
|
377
|
+
"gems_rb" => "gems.rb",
|
378
|
+
}
|
379
|
+
DEFAULT_EMPTY_DIRS = [ "app", "state" ]
|
380
|
+
|
381
|
+
def initialize(directory)
|
382
|
+
@location = directory
|
383
|
+
end
|
384
|
+
|
385
|
+
def gitignore_contents
|
386
|
+
<<~FILE_CONTENTS
|
387
|
+
# DGD Manifest files
|
388
|
+
.root
|
389
|
+
dgd.config
|
390
|
+
state/*
|
391
|
+
FILE_CONTENTS
|
392
|
+
end
|
393
|
+
|
394
|
+
def manifest_contents
|
395
|
+
<<FILE_CONTENTS
|
396
|
+
{
|
397
|
+
"name": "#{@name}",
|
398
|
+
"version": "0.1.0",
|
399
|
+
"description": "TODO: put description here",
|
400
|
+
"app_root": "app",
|
401
|
+
"goods": [
|
402
|
+
"# This is an example goods file - substitute your own.",
|
403
|
+
"https://raw.githubusercontent.com/noahgibbs/dgd-tools/main/goods/skotos_httpd.goods"
|
404
|
+
],
|
405
|
+
"unbundled_goods": [
|
406
|
+
{
|
407
|
+
"#": "this is an example of unbundled goods - substitute your own",
|
408
|
+
"name": "kernellib",
|
409
|
+
"git": {
|
410
|
+
"url": "https://github.com/ChatTheatre/kernellib.git",
|
411
|
+
"branch": "master"
|
412
|
+
},
|
413
|
+
"paths": {
|
414
|
+
"src/doc/kernel": "doc/kernel",
|
415
|
+
"src/include/kernel": "include/kernel",
|
416
|
+
"src/include/*.h": "include",
|
417
|
+
"src/kernel": "kernel"
|
418
|
+
}
|
419
|
+
}
|
420
|
+
]
|
421
|
+
}
|
422
|
+
FILE_CONTENTS
|
423
|
+
end
|
424
|
+
|
425
|
+
def gems_rb_contents
|
426
|
+
<<~FILE_CONTENTS
|
427
|
+
source "https://rubygems.org"
|
428
|
+
|
429
|
+
gem "dgd-tools", ">= #{DGD::VERSION}"
|
430
|
+
FILE_CONTENTS
|
431
|
+
end
|
432
|
+
|
433
|
+
def create!
|
434
|
+
if File.exist?(@location) && (!File.directory?(@location) || Dir["#{@location}/**"].size != 0)
|
435
|
+
raise "Cannot create a new DGD manifest project over a file or in an existing non-empty directory!"
|
436
|
+
end
|
437
|
+
|
438
|
+
puts "Creating new DGD manifest project at #{@location}..."
|
439
|
+
FileUtils.mkdir_p @location
|
440
|
+
Dir.chdir @location do
|
441
|
+
DEFAULT_FILE_LOCATIONS.each do |file_desc, file_location|
|
442
|
+
File.open(file_location, "wb") do |f|
|
443
|
+
contents = send("#{file_desc}_contents")
|
444
|
+
f.write(contents)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
DEFAULT_EMPTY_DIRS.each do |dir|
|
449
|
+
FileUtils.mkdir dir
|
450
|
+
FileUtils.touch File.join(dir, ".keep")
|
451
|
+
end
|
452
|
+
|
453
|
+
result = system "bundle"
|
454
|
+
raise("Could not run bundler to install dgd-tools for #{@location}!") unless result
|
455
|
+
|
456
|
+
result = system "bundle exec dgd-manifest install"
|
457
|
+
raise("Error when running dgd-manifest for #{@location}!") unless result
|
458
|
+
end
|
459
|
+
|
460
|
+
puts "Successfully created project at #{@location}."
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
class DGDRuntimeConfig
|
465
|
+
attr_reader :app_root
|
466
|
+
|
467
|
+
DEFAULT_CONFIG = {
|
468
|
+
users: 100,
|
469
|
+
editors: 40,
|
470
|
+
swap_size: 1048576,
|
471
|
+
sector_size: 512,
|
472
|
+
swap_fragment: 4096,
|
473
|
+
static_chunk: 64512,
|
474
|
+
dynamic_chunk: 261120,
|
475
|
+
dump_interval: 3600,
|
476
|
+
typechecking: 2,
|
477
|
+
include_file: "/include/std.h",
|
478
|
+
include_dirs: ["/include", "~/include"],
|
479
|
+
auto_object: "/kernel/lib/auto",
|
480
|
+
driver_object: "/kernel/sys/driver",
|
481
|
+
create: "_F_create",
|
482
|
+
array_size: 16384,
|
483
|
+
objects: 262144,
|
484
|
+
call_outs: 16384,
|
485
|
+
}
|
486
|
+
CONFIG_KEYS = DEFAULT_CONFIG.keys.map(&:to_s) + [ "app_root", "ports", "telnet_ports", "dump_file", "statedir" ]
|
487
|
+
|
488
|
+
def initialize(config_data)
|
489
|
+
@app_root = config_data["app_root"] || "app"
|
490
|
+
@ports = {
|
491
|
+
"*" => 50100,
|
492
|
+
}
|
493
|
+
@telnet_ports = {
|
494
|
+
"*" => 50110,
|
495
|
+
}
|
496
|
+
@statedir = config_data["statedir"] || "state"
|
497
|
+
@dump_file = if config_data["dump_file"]
|
498
|
+
"../" + config_data["dump_file"]
|
499
|
+
else
|
500
|
+
"../#{@statedir}/dump"
|
501
|
+
end
|
502
|
+
@config = DEFAULT_CONFIG.dup
|
503
|
+
|
504
|
+
@raw_data = config_data
|
505
|
+
@config.keys.each do |prop|
|
506
|
+
# For now, assume and require that JSON data is the correct type if present
|
507
|
+
@config[prop] = config_data[prop.to_s] if config_data[prop.to_s]
|
508
|
+
end
|
509
|
+
unexpected_config_keys = config_data.keys - CONFIG_KEYS
|
510
|
+
unless unexpected_config_keys.empty?
|
511
|
+
raise "Unexpected key names in DGD configuration: #{unexpected_config_keys.inspect}!"
|
512
|
+
end
|
513
|
+
|
514
|
+
if config_data["telnet_ports"]
|
515
|
+
@telnet_ports = config_to_ports(config_data["telnet_ports"])
|
516
|
+
end
|
517
|
+
if config_data["ports"]
|
518
|
+
@ports = config_to_ports(config_data["ports"])
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
def config_to_ports(data)
|
523
|
+
if data.is_a?(Hash)
|
524
|
+
# TODO: verify that keys are IP addr strings and values are legal port numbers
|
525
|
+
return data
|
526
|
+
elsif data.is_a?(Array)
|
527
|
+
# TODO: verify that data is an array of legal integer port numbers
|
528
|
+
ports = {}
|
529
|
+
data.each { |p| ports["*"] = p }
|
530
|
+
return ports
|
531
|
+
elsif data.is_a?(Integer)
|
532
|
+
return { "*": data }
|
533
|
+
else
|
534
|
+
raise "dgd-manifest: not sure how to get port data from a #{data.class.name} -- #{data.inspect}!"
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
def as_file
|
539
|
+
return <<DGD_CONFIG
|
540
|
+
telnet_port = ([
|
541
|
+
#{@telnet_ports.map { |ip, p| "#{ip.inspect}:#{p}" }.join(",\n ") }
|
542
|
+
]); /* legacy telnet ports */
|
543
|
+
binary_port = ([
|
544
|
+
#{@ports.map { |ip, p| "#{ip.inspect}:#{p}" }.join(",\n ") }
|
545
|
+
]); /* binary ports */
|
546
|
+
directory = "./#{GENERATED_ROOT}";
|
547
|
+
|
548
|
+
users = #{@config[:users]}; /* max # of connections */
|
549
|
+
editors = #{@config[:editors]}; /* max # of built-in-editor sessions */
|
550
|
+
ed_tmpfile = "../#{@statedir}/ed"; /* proto editor tmpfile */
|
551
|
+
swap_file = "../#{@statedir}/swap"; /* swap file */
|
552
|
+
swap_size = #{@config[:swap_size]}; /* # sectors in swap file */
|
553
|
+
sector_size = #{@config[:sector_size]}; /* swap sector size */
|
554
|
+
swap_fragment = #{@config[:swap_fragment]}; /* fragment to swap out */
|
555
|
+
static_chunk = #{@config[:static_chunk]}; /* static memory chunk */
|
556
|
+
dynamic_chunk = #{@config[:dynamic_chunk]}; /* dynamic memory chunk */
|
557
|
+
dump_file = #{@dump_file.inspect}; /* dump file */
|
558
|
+
dump_interval = #{@config[:dump_interval]}; /* expected statedump interval in seconds */
|
559
|
+
|
560
|
+
typechecking = #{@config[:typechecking]}; /* level of typechecking (2 is highest) */
|
561
|
+
include_file = #{@config[:include_file].inspect}; /* standard include file */
|
562
|
+
include_dirs = ({ #{@config[:include_dirs].map(&:inspect).join(", ")} }); /* directories to search */
|
563
|
+
auto_object = #{@config[:auto_object].inspect}; /* auto inherited object */
|
564
|
+
driver_object = #{@config[:driver_object].inspect}; /* driver object */
|
565
|
+
create = #{@config[:create].inspect}; /* name of create function */
|
566
|
+
|
567
|
+
array_size = #{@config[:array_size]}; /* max array size */
|
568
|
+
objects = #{@config[:objects]}; /* max # of objects */
|
569
|
+
call_outs = #{@config[:call_outs]}; /* max # of callouts */
|
570
|
+
DGD_CONFIG
|
284
571
|
end
|
285
572
|
end
|
286
573
|
end
|