rbuildsys 0.0.1.pre
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
- data/lib/rbuildsys.rb +410 -0
- metadata +47 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 907496c16fb0c9c5b7ee4fcc5a177cdf4d25f58603c952e012b2d9304355fb0d
|
4
|
+
data.tar.gz: 20044f4d5b9ff2627780e2c43c3d623adf1137e3171ce9945cdee35d787c6c4e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 81c674e67e81835bcae6a17bbd632c31f25b99aa81b5d4f5421b5fc655ddf269976ecf6944309dd36306133f8b26810aae2222dece06d683807bc3cdcccb0eeb
|
7
|
+
data.tar.gz: 6b882e5244940eff79357dcff6df5bc10b8d9c96a3fb8e5e18432f78feb4aa0d4ed9a47058fd853ea8072f30cecc20ec0f761c018a25a59a56fbf46455f3959b
|
data/lib/rbuildsys.rb
ADDED
@@ -0,0 +1,410 @@
|
|
1
|
+
require "optparse"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
# Namespace for all code for RBuildSys
|
5
|
+
module RBuildSys
|
6
|
+
|
7
|
+
# Class to describe a buildable RBuildSys project
|
8
|
+
#
|
9
|
+
# @author Mai-Lapyst
|
10
|
+
class Project
|
11
|
+
# Returns the name of the project
|
12
|
+
# @return [String]
|
13
|
+
attr_reader :name
|
14
|
+
|
15
|
+
# Returns the array of source directorys for this project
|
16
|
+
# @return [Array<String>]
|
17
|
+
attr_reader :src_dirs
|
18
|
+
|
19
|
+
# Returns the array of include directorys for this project
|
20
|
+
# @return [Array<String>]
|
21
|
+
attr_reader :inc_dirs
|
22
|
+
|
23
|
+
# Returns the array of library directorys for this project
|
24
|
+
# @return [Array<String>]
|
25
|
+
attr_reader :lib_dirs
|
26
|
+
|
27
|
+
# Returns the array of librarys for this project
|
28
|
+
# @return [Array<String>]
|
29
|
+
attr_reader :librarys
|
30
|
+
|
31
|
+
# Returns the array of flags for this project; will be added to the compiler call
|
32
|
+
# @return [Array<String>]
|
33
|
+
attr_reader :flags
|
34
|
+
|
35
|
+
# Returns true if the project is install-able, false otherwise
|
36
|
+
# @return [true, false]
|
37
|
+
attr_accessor :no_install
|
38
|
+
|
39
|
+
# Returns the library type of this project; if nil, this project isn't a library
|
40
|
+
# @return [:static, :dynamic, :s, :dyn]
|
41
|
+
attr_accessor :libType
|
42
|
+
|
43
|
+
# Returns the dependencys of this project
|
44
|
+
# @return [Array<Project>]
|
45
|
+
attr_reader :dependencys
|
46
|
+
|
47
|
+
# @param name [String] name of the project; see {#name}
|
48
|
+
# @param options [Hash] various options for the project
|
49
|
+
# @option options :lang [Symbol] language used for this project. Available: +:c+, +:cpp+
|
50
|
+
# @option options :srcFile_endings [Array<String>] additional fileendings that should be used for finding sourcefiles
|
51
|
+
# @option options :no_install [true, false] flag that tells if the project is install-able or not; see {#no_install}
|
52
|
+
# @option options :libType [:static, :dynamic, :s, :dyn, nil] type of library; see {#libType}
|
53
|
+
# @option options :compiler [String, nil] compiler to be used to transform sourcefiles to objectfiles; if no is supplied, +"gcc"+ or +"g++"+ is used (based on the language)
|
54
|
+
def initialize(name, options)
|
55
|
+
@name = name;
|
56
|
+
@src_dirs = [];
|
57
|
+
@inc_dirs = [];
|
58
|
+
@lib_dirs = [];
|
59
|
+
@dependencys = [];
|
60
|
+
@librarys = [];
|
61
|
+
@flags = [];
|
62
|
+
|
63
|
+
check_lang(options[:lang] || "c");
|
64
|
+
|
65
|
+
@compiler = "gcc" if (@lang == :c);
|
66
|
+
@compiler = "g++" if (@lang == :cpp);
|
67
|
+
if (options[:compiler]) then
|
68
|
+
@compiler = options[:compiler];
|
69
|
+
end
|
70
|
+
|
71
|
+
@src_file_endings = [];
|
72
|
+
@src_file_endings = ["cpp", "cc", "c++", "cxx"] if (@lang == :cpp);
|
73
|
+
@src_file_endings = ["c"] if (@lang == :c);
|
74
|
+
|
75
|
+
if (options[:srcFile_endings]) then
|
76
|
+
@src_file_endings.push(*options[:srcFile_endings]);
|
77
|
+
@src_file_endings.uniq!;
|
78
|
+
end
|
79
|
+
|
80
|
+
@no_install = options[:no_install] || false;
|
81
|
+
@libType = options[:libType];
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
# lang must be a valid string for the gcc/g++ -std option:
|
86
|
+
# https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html
|
87
|
+
# or simply the language: c, c++, gnu, gnu++
|
88
|
+
def check_lang(lang)
|
89
|
+
if ((m = lang.match(/^(c\+\+|gnu\+\+)([\dxyza]+)?$/)) != nil) then
|
90
|
+
@lang = :cpp;
|
91
|
+
@c_standard = lang if (m[2]);
|
92
|
+
return;
|
93
|
+
end
|
94
|
+
|
95
|
+
if ((m = lang.match(/^(c|gnu)([\dx]+)?$/)) != nil) then
|
96
|
+
@lang = :c;
|
97
|
+
@c_standard = lang if (m[2]);
|
98
|
+
return;
|
99
|
+
end
|
100
|
+
|
101
|
+
if (lang.match(/^iso(\d+):([\dx]+)$/) != nil) then
|
102
|
+
@lang = :c;
|
103
|
+
@c_standard = lang;
|
104
|
+
return;
|
105
|
+
end
|
106
|
+
|
107
|
+
raise ArgumentError.new("argument #2 contains key lang, but cannot validate it: '#{lang}'");
|
108
|
+
end
|
109
|
+
|
110
|
+
public
|
111
|
+
# cleans the project's output directory
|
112
|
+
def clean()
|
113
|
+
buildDir = "./build/#{name}";
|
114
|
+
FileUtils.remove_dir(buildDir) if File.directory?(buildDir)
|
115
|
+
end
|
116
|
+
|
117
|
+
public
|
118
|
+
# builds the project
|
119
|
+
# @return [true, false] state of the build, true means success, false means failure
|
120
|
+
def build()
|
121
|
+
for dep in dependencys do
|
122
|
+
if (!dep.build()) then
|
123
|
+
return false;
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
puts "Now building '#{@name}'";
|
128
|
+
puts "- using language #{@lang}";
|
129
|
+
|
130
|
+
for dep in dependencys do
|
131
|
+
@inc_dirs.push(*dep.inc_dirs).uniq!;
|
132
|
+
@lib_dirs.push("./build/#{dep.name}");
|
133
|
+
@librarys.push(dep.name);
|
134
|
+
if (dep.libType == :static) then
|
135
|
+
@lib_dirs.push(*dep.lib_dirs).uniq!
|
136
|
+
@librarys.push(*dep.librarys).uniq!
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
@flags.push("-g") if (OPTIONS[:debug]);
|
141
|
+
|
142
|
+
# create the project build dir
|
143
|
+
buildDir = "./build/#{name}";
|
144
|
+
system("mkdir -p #{buildDir}"); # todo: dont use system!
|
145
|
+
|
146
|
+
# make the includes
|
147
|
+
#inc_dirs.map! { |incDir| incDir + "/*.h" }
|
148
|
+
#includes = Dir.glob(inc_dirs);
|
149
|
+
includes = @inc_dirs.map{|incDir| "-I #{incDir}"}.join(" ");
|
150
|
+
libs = @lib_dirs.map{|libDir| "-L #{libDir}"}.join(" ") + " " + @librarys.map{|lib| "-l #{lib}"}.join(" ");
|
151
|
+
std = @c_standard ? ("-std=#{@c_standard}") : "";
|
152
|
+
|
153
|
+
# for now, just ignore all global and relative paths for sources
|
154
|
+
src_dirs.select! { |srcDir|
|
155
|
+
srcDir[0] != "/" && !srcDir.start_with?("../")
|
156
|
+
}
|
157
|
+
|
158
|
+
source_modified = false;
|
159
|
+
build_failed = false;
|
160
|
+
|
161
|
+
src_dirs.each { |srcDir|
|
162
|
+
globStr = File.join(srcDir, "**/*.{#{@src_file_endings.join(",")}}");
|
163
|
+
srcFiles = Dir.glob(globStr);
|
164
|
+
|
165
|
+
# do the incremental build!
|
166
|
+
srcFiles.each { |srcFile|
|
167
|
+
objFile = File.join(buildDir, srcFile.gsub(srcDir, ""));
|
168
|
+
objFile = objFile.gsub(Regexp.new("\.(#{@src_file_endings.join("|")})$"), ".o");
|
169
|
+
srcTime = File.mtime(srcFile);
|
170
|
+
|
171
|
+
if (OPTIONS[:cleanBuild] || !File.exists?(objFile) || (srcTime > File.mtime(objFile))) then
|
172
|
+
source_modified = true;
|
173
|
+
FileUtils.mkdir_p(File.dirname(objFile)); # ensure we have the parent dir(s)
|
174
|
+
|
175
|
+
# build the source!
|
176
|
+
cmd = "#{@compiler} #{std} -Wall #{includes} #{@flags.join(" ")} -c -o #{objFile} #{srcFile}";
|
177
|
+
puts "- $ #{cmd}";
|
178
|
+
f = system(cmd);
|
179
|
+
if (f) then
|
180
|
+
FileUtils.touch(objFile, :mtime => srcTime);
|
181
|
+
else
|
182
|
+
build_failed = true;
|
183
|
+
end
|
184
|
+
end
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
if (build_failed) then
|
189
|
+
return false;
|
190
|
+
end
|
191
|
+
|
192
|
+
if (!source_modified) then
|
193
|
+
puts "- No need for building library/executable, nothing changed!";
|
194
|
+
return true;
|
195
|
+
end
|
196
|
+
|
197
|
+
objFiles = Dir.glob(buildDir + "/**/*.o");
|
198
|
+
if (libType == :static) then
|
199
|
+
puts "- Building static library lib#{name}!";
|
200
|
+
libname = File.join(buildDir, "lib#{name}.a");
|
201
|
+
cmd = "ar rcs #{libname} #{objFiles.join(" ")}";
|
202
|
+
puts " - $ #{cmd}";
|
203
|
+
f = system(cmd);
|
204
|
+
return f;
|
205
|
+
elsif (libType == :dynamic) then
|
206
|
+
raise NotImplementedError.new("");
|
207
|
+
else
|
208
|
+
# build runable binary
|
209
|
+
binname = File.join(buildDir, "#{name}.run");
|
210
|
+
puts "- Building executable #{binname}!";
|
211
|
+
cmd = "#{@compiler} #{includes} #{@flags.join(" ")} -o #{binname} #{objFiles.join(" ")} #{libs}";
|
212
|
+
puts " - $ #{cmd}";
|
213
|
+
f = system(cmd);
|
214
|
+
return f;
|
215
|
+
end
|
216
|
+
|
217
|
+
return true;
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# Stores all projects that are defined by the user
|
222
|
+
PROJECTS = {};
|
223
|
+
|
224
|
+
# Holds the current project that is currently being configured.
|
225
|
+
# Only non-nil inside the block for {#newProject}
|
226
|
+
@@cur_proj = nil;
|
227
|
+
|
228
|
+
# Stores all options; will be modified through the commandline option parser
|
229
|
+
# @see @@optparse
|
230
|
+
OPTIONS = {
|
231
|
+
# true if the current build should be a debug build
|
232
|
+
:debug => true,
|
233
|
+
|
234
|
+
# true if we only want to clean our build directory
|
235
|
+
:clean => false,
|
236
|
+
|
237
|
+
# true if we want to clean all buildfiles before building
|
238
|
+
:cleanBuild => false,
|
239
|
+
|
240
|
+
# true if we want to install instead of building
|
241
|
+
:install => false,
|
242
|
+
};
|
243
|
+
|
244
|
+
# Option parser of RBuildSys, used to provide standard operations such as clean, clean build, install...
|
245
|
+
@@optparse = OptionParser.new do |opts|
|
246
|
+
opts.banner = "Usage: #{$PROGRAM_NAME} [options] <projects>";
|
247
|
+
|
248
|
+
opts.on("-r","--release", "Build for release") do
|
249
|
+
# todo: if compiled for debug before, the system cannot detect the change
|
250
|
+
OPTIONS[:debug] = false;
|
251
|
+
end
|
252
|
+
opts.on("-b", "--build-clean", "Make a clean build") do
|
253
|
+
OPTIONS[:cleanBuild] = true;
|
254
|
+
end
|
255
|
+
opts.on("-c", "--clean", "Clean all projects build dirs") do
|
256
|
+
OPTIONS[:clean] = true;
|
257
|
+
end
|
258
|
+
opts.on("-i", "--install", "Install all project(s) that are installable") do
|
259
|
+
OPTIONS[:install] = true;
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# creates a new project to build
|
264
|
+
# takes a block to execute, that defines the project. it is immediately executed,
|
265
|
+
# and has aaccess to the current project (that is created with this method) in {RBuildSys::@@cur_proj RBuildSys::@@cur_proj}.
|
266
|
+
#
|
267
|
+
# @yield Block to configure the project
|
268
|
+
#
|
269
|
+
# @param name [String] name of the project, also used for the final output of the project
|
270
|
+
# @param options [Hash] various options; for details see {Project#initialize}
|
271
|
+
def newProject(name, options = {})
|
272
|
+
raise ArgumentError.new("name need to be a string") if (name.class != String);
|
273
|
+
@@cur_proj = Project.new(name, options);
|
274
|
+
yield
|
275
|
+
PROJECTS[name] = @@cur_proj;
|
276
|
+
@@cur_proj = nil;
|
277
|
+
end
|
278
|
+
|
279
|
+
# adds include directorys to use for the current project
|
280
|
+
#
|
281
|
+
# @note should only be called inside the block for {#newProject}
|
282
|
+
# @param dir [String, Array<String>] directory path; root of the include path. if its a Array, each element is used in a call to {#incDir}
|
283
|
+
# @param more [Array<String>] additional include paths; each element is used in a call to {#incDir}
|
284
|
+
def incDir(dir, *more)
|
285
|
+
if (dir.class == String) then
|
286
|
+
raise ArgumentError.new("argument #1 is no directory: '#{dir}'") if (!Dir.exists?(dir));
|
287
|
+
@@cur_proj.inc_dirs.push(dir);
|
288
|
+
elsif (dir.class == Array) then
|
289
|
+
dir.each { |d| incDir(d) }
|
290
|
+
else
|
291
|
+
raise ArgumentError.new("argument #1 must be a String or an Array");
|
292
|
+
end
|
293
|
+
more.each { |d| incDir(d) }
|
294
|
+
end
|
295
|
+
|
296
|
+
# adds source directorys to use for the current project
|
297
|
+
#
|
298
|
+
# @note should only be called inside the block for {#newProject}
|
299
|
+
# @param dir [String, Array<String>] directory path; root of the source path. if its a Array, each element is used in a call to {#srcDir}
|
300
|
+
# @param more [Array<String>] additional source paths; each element is used in a call to {#srcDir}
|
301
|
+
def srcDir(dir, *more)
|
302
|
+
if (dir.class == String) then
|
303
|
+
raise ArgumentError.new("argument #1 is no directory: '#{dir}'") if (!Dir.exists?(dir));
|
304
|
+
@@cur_proj.src_dirs.push(dir);
|
305
|
+
elsif (dir.class == Array) then
|
306
|
+
dir.each { |d| srcDir(d) }
|
307
|
+
else
|
308
|
+
raise ArgumentError.new("argument #1 must be a String or an Array");
|
309
|
+
end
|
310
|
+
more.each { |d| srcDir(d) }
|
311
|
+
end
|
312
|
+
|
313
|
+
# tells the current project that it is not install-able
|
314
|
+
# this means that if you run an install of your projects, this one will not be installed
|
315
|
+
#
|
316
|
+
# @note should only be called inside the block for {#newProject}
|
317
|
+
def noInstall()
|
318
|
+
@@cur_proj.no_install = true;
|
319
|
+
end
|
320
|
+
|
321
|
+
# sets the current project as a lib
|
322
|
+
#
|
323
|
+
# @note should only be called inside the block for {#newProject}
|
324
|
+
# @param type [:static, :dynamic, :s, :dyn] the type of the library: static (*.a / *.lib) or dynamic (*.so / *.dll)
|
325
|
+
def isLib(type)
|
326
|
+
@@cur_proj.libType = :static if ([:static, :s].include?(type));
|
327
|
+
@@cur_proj.libType = :dynamic if ([:dynamic, :dyn, :d].include?(type));
|
328
|
+
end
|
329
|
+
|
330
|
+
# links a RBuildSys project to the current project
|
331
|
+
# this has the effect that the given project is build before the current project, and
|
332
|
+
# if the given project is a lib, it is also linked to the current project
|
333
|
+
#
|
334
|
+
# @note should only be called inside the block for {#newProject}
|
335
|
+
# @param name [String] can be a local project (inside current file) or any installed project
|
336
|
+
def use(name)
|
337
|
+
if (PROJECTS[name] == nil) then
|
338
|
+
# use installed project
|
339
|
+
raise NotImplementedError.new("");
|
340
|
+
else
|
341
|
+
# use local
|
342
|
+
proj = PROJECTS[name];
|
343
|
+
end
|
344
|
+
|
345
|
+
if (!proj.libType) then
|
346
|
+
raise ArgumentError.new("argument #1 can't be used cause it is not a library");
|
347
|
+
end
|
348
|
+
|
349
|
+
@@cur_proj.dependencys.push(proj);
|
350
|
+
end
|
351
|
+
|
352
|
+
# adds a external library to the current project
|
353
|
+
#
|
354
|
+
# @note should only be called inside the block for {#newProject}
|
355
|
+
# @note there is currently no check in place to verify that the given library exists
|
356
|
+
# @param name [String] name of the library that should be used. in a c/c++ context, it should be without the leading "lib"
|
357
|
+
# @param path [String, nil] optional search path for this library
|
358
|
+
def useLib(name, path = nil)
|
359
|
+
@@cur_proj.librarys.push(name);
|
360
|
+
@@cur_proj.lib_dirs.push(path) if (path != nil && Dir.exists?(path));
|
361
|
+
end
|
362
|
+
|
363
|
+
# adds an option to the option parser of RBuildSys
|
364
|
+
# @see OptionParser#on
|
365
|
+
def onOption(*opts, &block)
|
366
|
+
@@optparse.on(*opts, block);
|
367
|
+
end
|
368
|
+
|
369
|
+
# adds an option to the option parser of RBuildSys
|
370
|
+
# @see OptionParser#on_tail
|
371
|
+
def onTailOption(*opts, &block)
|
372
|
+
@@optparse.on_tail(*opts, block);
|
373
|
+
end
|
374
|
+
|
375
|
+
# adds an option to the option parser of RBuildSys
|
376
|
+
# @see OptionParser#on_head
|
377
|
+
def onHeadOption(*opts, &block)
|
378
|
+
@@optparse.on_head(*opts, block);
|
379
|
+
end
|
380
|
+
|
381
|
+
# adds a flag to the flags of the current project.
|
382
|
+
#
|
383
|
+
# @note should only be called inside the block for {#newProject}
|
384
|
+
# @param flag [String] the flag to be added
|
385
|
+
def flag(flag)
|
386
|
+
@@cur_proj.flags.push(flag);
|
387
|
+
end
|
388
|
+
|
389
|
+
# begins the build process.
|
390
|
+
# should be called after you configured all your projects with {#newProject}.
|
391
|
+
def build()
|
392
|
+
@@optparse.parse!
|
393
|
+
if (ARGV.length == 0) then
|
394
|
+
puts @@optparse;
|
395
|
+
exit(1);
|
396
|
+
end
|
397
|
+
|
398
|
+
ARGV.each { |projname|
|
399
|
+
proj = PROJECTS[projname];
|
400
|
+
if (proj) then
|
401
|
+
if (OPTIONS[:clean]) then
|
402
|
+
proj.clean();
|
403
|
+
else
|
404
|
+
proj.build();
|
405
|
+
end
|
406
|
+
end
|
407
|
+
}
|
408
|
+
end
|
409
|
+
|
410
|
+
end
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rbuildsys
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.pre
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mai-Lapyst
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-11-06 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |2
|
14
|
+
rbuildsys is a build system like cmake,
|
15
|
+
where you write your own scripts to define
|
16
|
+
how your project needs to be build
|
17
|
+
email:
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- lib/rbuildsys.rb
|
23
|
+
homepage: https://rubygems.org/gems/rbuildsys
|
24
|
+
licenses:
|
25
|
+
- GPL-3.0
|
26
|
+
metadata: {}
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.3.1
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 2.7.6.2
|
44
|
+
signing_key:
|
45
|
+
specification_version: 4
|
46
|
+
summary: Scriptable build system, written in ruby
|
47
|
+
test_files: []
|