canoe 0.3.0.2 → 0.3.2.2

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.
@@ -1,26 +1,28 @@
1
- class WorkSpace
2
- def clean
3
- self.send "clean_#{@mode.to_s}"
4
- end
5
-
6
- private
7
- def clean_obj
8
- puts "rm -f ./obj/*.o"
9
- system "rm -f ./obj/*.o"
10
- end
1
+ module Canoe
2
+ class WorkSpace
3
+ # valid options: none, 'all', 'target', 'tests'
4
+ def clean(arg = 'all')
5
+ send "clean_#{arg}"
6
+ end
11
7
 
12
- def clean_target
13
- puts "rm -f ./target/*"
14
- system "rm -f ./target/*"
15
- end
8
+ private
16
9
 
17
- def clean_bin
18
- clean_obj
19
- clean_target
20
- end
10
+ def clean_all
11
+ clean_target
12
+ clean_obj
13
+ end
14
+
15
+ def clean_target
16
+ issue_command 'rm ./target/* -rf'
17
+ end
18
+
19
+ def clean_obj
20
+ issue_command 'rm ./obj/* -rf'
21
+ end
21
22
 
22
- def clean_lib
23
- clean_obj
24
- clean_target
23
+ def clean_tests
24
+ issue_command 'rm ./obj/test_* -rf'
25
+ issue_command 'rm ./target/test_* -rf'
26
+ end
25
27
  end
26
- end
28
+ end
data/lib/workspace/dep.rb CHANGED
@@ -1,12 +1,14 @@
1
- class WorkSpace
2
- def dep
3
- deps = DepAnalyzer.read_from(@deps) if File.exist?(@deps)
4
- deps.each do |k, v|
5
- unless v.empty?
1
+ module Canoe
2
+ class WorkSpace
3
+ def dep
4
+ deps = DepAnalyzer.read_from(@deps) if File.exist?(@deps)
5
+ deps.each do |k, v|
6
+ next if v.empty?
7
+
6
8
  puts "#{k.blue} depends on: "
7
- v.each {|f| puts " #{f.blue}"}
8
- puts ""
9
+ v.each { |f| puts " #{f.blue}" }
10
+ puts ''
9
11
  end
10
12
  end
11
13
  end
12
- end
14
+ end
@@ -1,6 +1,10 @@
1
- class WorkSpace
2
- def generate
3
- DepAnalyzer.new('./src', @source_suffix, @header_suffix)
4
- .build_to_file ['./src', './src/components'], @deps
1
+ module Canoe
2
+ class WorkSpace
3
+ def generate
4
+ DepAnalyzer.new(@src_short, @source_suffix, @header_suffix)
5
+ .build_to_file [@src_short, @components_short], @deps
6
+ DepAnalyzer.new(@tests_short, @source_suffix, @header_suffix)
7
+ .build_to_file [@src_short, @components_short], @test_deps
8
+ end
5
9
  end
6
10
  end
@@ -1,71 +1,84 @@
1
- class WorkSpace
2
- def self.help
3
- info = <<~INFO
4
- canoe is a C/C++ project manager, inspired by Rust cargo.
5
- usage:
6
- canoe new tada: create a project named 'tada' in current directory
7
-
8
- canoe build: compile current project (execute this command in project directory)
9
-
10
- canoe generate: generate dependency relationships and store it in '.canoe.deps' file. Alias: update
1
+ module Canoe
2
+ class WorkSpace
3
+ def self.help
4
+ info = <<~INFO
5
+ canoe is a C/C++ project manager, inspired by Rust cargo.
6
+ usage:
7
+ canoe new tada: create a project named 'tada' in current directory
8
+
9
+ canoe build: compile current project (execute this command in project directory)
11
10
 
12
- canoe update: udpate dependency relationships and store it in '.canoe.deps' file.
13
-
14
- canoe run: compile and execute current project (execute this command in project directory)
15
-
16
- canoe clean: remove all generated object files and binary files
17
-
18
- canoe help: show this help message
19
-
20
- canoe add tada: add a folder named tada under workspace/components,
21
-
22
- canoe dep: show current dependency relationships of current project
23
-
24
- canoe verion: version information
25
-
26
- new project_name [mode] [suffixes]:
27
- create a new project with project_name.
28
- In this project, four directories obj, src, target and third-party will be generated in project directory.
29
- in src, directory 'components' will be generated if [mode] is '--lib', an extra main.cpp will be generated if [mode] is '--bin'
30
-
31
- [mode]: --lib for a library and --bin for executable binaries
32
- [suffixes]: should be in 'source_suffix:header_suffix" format, notice the ':' between two suffixes
33
- add component_name:
34
- add a folder named tada under workspace/components.
35
- two files tada.hpp and tada.cpp would be craeted and intialized. File suffix may differ according users' specifications.
36
- if component_name is a path separated by '/', then canoe would create folders and corresponding files recursively.
37
-
38
- generate:
39
- generate dependence relationship for each file, this may accelarate
40
- `canoe buid` command. It's recommanded to execute this command everytime
41
- headers are added or removed from any file.
42
-
43
- update:
44
- this command is needed because '.canoe.deps' is actually a cache of dependency relationships so that canoe doesn't have to analyze all the files when building a project.
45
- So when a file includes new headers or some headers are removed, users have to use 'canoe udpate'
46
- to update dependency relationships.
47
-
48
- build [options]:
49
- build current project, arguments in [options] will be passed to C++ compiler
11
+ canoe test: build and run tests
12
+
13
+ canoe generate: generate dependency relationships and store it in '.canoe.deps' file. Alias: update
14
+
15
+ canoe update: udpate dependency relationships and store it in '.canoe.deps' file.
50
16
 
51
- run [options]:
52
- build current project with no specific compilation flags, and run this project, passing [options] as command line arguments to the binary
53
-
54
- clean:
55
- remove all generated object files and binary files
56
-
57
- help:
58
- show this help message
59
-
60
- verion:
61
- display version information
62
-
63
- dep:
64
- display file dependencies in a better readable way
65
-
66
- @author: written by XIONG Ziwei, ICT, CAS
67
- @contact: noahxiong@outlook.com
68
- INFO
69
- puts info
17
+ canoe run: compile and execute current project (execute this command in project directory)
18
+
19
+ canoe clean: remove all generated object files and binary files
20
+
21
+ canoe help: show this help message
22
+
23
+ canoe add tada: add a folder named tada under workspace/components,
24
+
25
+ canoe dep: show current dependency relationships of current project
26
+
27
+ canoe verion: version information
28
+
29
+ canoe make: generate a makefile for this project
30
+
31
+ new project_name [mode] [suffixes]:
32
+ create a new project with project_name.
33
+ In this project, four directories obj, src, target and third-party will be generated in project directory.
34
+ in src, directory 'components' will be generated if [mode] is '--lib', an extra main.cpp will be generated if [mode] is '--bin'
35
+
36
+ [mode]: --lib for a library and --bin for executable binaries
37
+ [suffixes]: should be in 'source_suffix:header_suffix" format, notice the ':' between two suffixes
38
+ add component_name:
39
+ add a folder named tada under workspace/components.
40
+ two files tada.hpp and tada.cpp would be craeted and intialized. File suffix may differ according users' specifications.
41
+ if component_name is a path separated by '/', then canoe would create folders and corresponding files recursively.
42
+
43
+ generate:
44
+ generate dependence relationship for each file, this may accelarate
45
+ `canoe buid` command. It's recommanded to execute this command everytime
46
+ headers are added or removed from any file.
47
+
48
+ update:
49
+ this command is needed because '.canoe.deps' is actually a cache of dependency relationships so that canoe doesn't have to analyze all the files when building a project.
50
+ So when a file includes new headers or some headers are removed, users have to use 'canoe udpate'
51
+ to update dependency relationships.
52
+
53
+ build [all|test]:
54
+ build current project, 'all' builds both target and tests, 'test' builds tests only
55
+
56
+ test [tests]:
57
+ build and run tests
58
+ [tests]: 'all' for all tests, or a name of a test for a single test
59
+
60
+ run [options]:
61
+ build current project with no specific compilation flags, and run this project, passing [options] as command line arguments to the binary
62
+
63
+ clean:
64
+ remove all generated object files and binary files
65
+
66
+ help:
67
+ show this help message
68
+
69
+ verion:
70
+ display version information
71
+
72
+ dep:
73
+ display file dependencies in a better readable way
74
+
75
+ make:
76
+ generate a Makefile for this project
77
+
78
+ @author: written by XIONG Ziwei, ICT, CAS
79
+ @contact: noahxiong@outlook.com
80
+ INFO
81
+ puts info
82
+ end
70
83
  end
71
- end
84
+ end
@@ -0,0 +1,279 @@
1
+ # If the project has circular dependency, this command would fail
2
+ module Canoe
3
+ ##
4
+ # CanoeMakefile is used to offer makefile generation utilities
5
+ class CanoeMakefile
6
+ include WorkSpaceUtil
7
+ def initialize(workspace)
8
+ @workspace = workspace
9
+ @all_names = []
10
+ @common_variables = {}
11
+ @src_variables = {}
12
+ @hdr_variables = {}
13
+ @obj_variables = {}
14
+ @config = {}
15
+ end
16
+
17
+ def configure(config)
18
+ @config = config
19
+ end
20
+
21
+ def make!(deps)
22
+ File.open('Makefile', 'w') do |f|
23
+ if cxx?(get_compiler)
24
+ make_cxx(f, deps)
25
+ else
26
+ make_c(f, deps)
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def get_compiler
34
+ @config['compiler']
35
+ end
36
+
37
+ def get_header_suffix
38
+ @workspace.header_suffix
39
+ end
40
+
41
+ def get_source_suffix
42
+ @workspace.source_suffix
43
+ end
44
+
45
+ def get_compiling_flags
46
+ flags = @config['flags']['compile'].values.join ' '
47
+ flags + ' -I./src/components'
48
+ end
49
+
50
+ def get_ldflags
51
+ @config['flags']['link'].values.select { |v| v.start_with?('-L') }.join ' '
52
+ end
53
+
54
+ def get_ldlibs
55
+ (@config['flags']['link'].values - (get_ldflags.split)).join ' '
56
+ end
57
+
58
+ def cxx?(name)
59
+ return get_compiler.end_with? '++'
60
+ end
61
+
62
+ def make_cxx(makefile, deps)
63
+ make_common(makefile, 'CXX', deps)
64
+ end
65
+
66
+ def make_c(makefile, deps)
67
+ make_common(makefile, 'CC', deps)
68
+ end
69
+
70
+ def make_common(makefile, compiler_prefix, deps)
71
+ make_compiling_info(makefile, compiler_prefix)
72
+ define_variables(makefile, deps)
73
+ make_rules(makefile, deps)
74
+ end
75
+
76
+ def make_compiling_info(makefile, compiler_prefix)
77
+ makefile.puts("#{compiler_prefix}=#{get_compiler}")
78
+ makefile.puts("#{compiler_prefix}FLAGS=#{get_compiling_flags}")
79
+ makefile.puts("LDFLAGS=#{get_ldflags}")
80
+ makefile.puts("LDLIBS=#{get_ldlibs}")
81
+ makefile.puts ''
82
+ end
83
+
84
+ def define_variables(makefile, deps)
85
+ define_dirs(makefile)
86
+ src_files = deps.keys.select { |f| f.end_with? get_source_suffix }
87
+
88
+ generate_all_names(src_files)
89
+ define_srcs(makefile, src_files)
90
+ makefile.puts ''
91
+ define_hdrs(makefile, src_files)
92
+ makefile.puts ''
93
+ define_objs(makefile, src_files)
94
+ makefile.puts ''
95
+ define_tests(makefile, src_files)
96
+ makefile.puts ''
97
+ end
98
+
99
+ def extract_name(name, _)
100
+ File.basename(file_to_obj(name), '.*')
101
+ end
102
+
103
+ def generate_all_names(files)
104
+ files.each do |f|
105
+ name = extract_name(f, @workspace.components_prefix).upcase
106
+ @all_names << name
107
+ @src_variables[name] = f
108
+ @hdr_variables[name] = f.gsub @workspace.source_suffix, @workspace.header_suffix
109
+ @obj_variables[name] = file_to_obj(f)
110
+ end
111
+ end
112
+
113
+ def define_dirs(makefile)
114
+ makefile.puts("TARGET_DIR=./target")
115
+ if @workspace.mode == :bin
116
+ makefile.puts("TARGET=$(TARGET_DIR)/#{@workspace.name}")
117
+ else
118
+ makefile.puts("TARGET=$(TARGET_DIR)/lib#{@workspace.name.downcase}.so")
119
+ end
120
+ # note the ending slash
121
+ makefile.puts("OBJ_DIR=#{@workspace.obj_prefix[..-2]}")
122
+ makefile.puts("SRC_DIR=#{@workspace.src_prefix[..-2]}")
123
+ makefile.puts("COMPONENTS_DIR=#{@workspace.components_prefix[..-2]}")
124
+ makefile.puts ""
125
+ end
126
+
127
+ def define_srcs(makefile, files)
128
+ @src_variables.each do |k, v|
129
+ makefile.puts("SRC_#{k}=#{v}")
130
+ end
131
+ end
132
+
133
+ def define_hdrs(makefile, files)
134
+ @hdr_variables.each do |k, v|
135
+ next if k == "MAIN"
136
+ makefile.puts("HDR_#{k}=#{v}") if File.exist? v
137
+ end
138
+ end
139
+
140
+ def define_objs(makefile, files)
141
+ @obj_variables.each do |k, v|
142
+ makefile.puts("OBJ_#{k}=#{v}")
143
+ end
144
+ objs = @obj_variables.keys.map { |k| "$(OBJ_#{k})" }
145
+ bin_objs = objs.reject { |o| o.start_with? '$(OBJ_TEST'}
146
+ test_objs = objs - bin_objs
147
+ makefile.puts ''
148
+ makefile.puts("OUT_OBJS=#{bin_objs.join ' '}")
149
+ makefile.puts("TEST_OBJS=#{test_objs.join ' '}")
150
+ end
151
+
152
+ def define_tests(makefile, files)
153
+ test_files = files.select { |f| File.basename(f, '.*').start_with? 'test_'}
154
+ test_files.each do |f|
155
+ basename = File.basename(f, '.*')
156
+ test = "#{@workspace.target_short}/#{basename}"
157
+ makefile.puts("#{basename.upcase}=#{test}")
158
+ end
159
+ tests = test_files.map do |f|
160
+ "$(#{File.basename(f, '.*').upcase})"
161
+ end
162
+ makefile.puts("TESTS=#{tests.join ' '}")
163
+ end
164
+
165
+ def get_all_dep_name(file_name, deps)
166
+ dep = deps[file_name]
167
+ if dep.empty?
168
+ []
169
+ else
170
+ tmp = dep.map { |n| extract_name(n, @workspace.components_prefix).upcase }
171
+ dep.each do |d|
172
+ tmp += get_all_dep_name(d, deps)
173
+ end
174
+ tmp
175
+ end
176
+ end
177
+
178
+ def emit_dependencies(makefile, name, deps)
179
+ as_str = deps.map do |n|
180
+ if n == name
181
+ ["$(SRC_#{n})"] + ["$(HDR_#{n})"] * (name == "MAIN" ? 0 : 1)
182
+ else
183
+ "$(#{n}_DEP)"
184
+ end
185
+ end.flatten.join " "
186
+ makefile.puts("#{name}_DEP=#{as_str}")
187
+ end
188
+
189
+ def make_dependencies(makefile, deps)
190
+ dep_variables = Hash[@all_names.map { |n| [n, []] }]
191
+ reference = Hash[@all_names.map { |n| [n, []] }]
192
+ @all_names.each do |n|
193
+ dep_variables[n] = ([n] + get_all_dep_name(@src_variables[n], deps)).uniq
194
+ reference[n] = ([n] + get_all_dep_name(@src_variables[n], deps)).uniq
195
+ end
196
+
197
+ # deduplication
198
+ dep_variables.each do |k, v|
199
+ v.each do |n|
200
+ next if n == k
201
+ v = v - reference[n] + [n] if v.include? n
202
+ end
203
+ dep_variables[k] = v
204
+ end
205
+
206
+ dep_variables.each do |k, v|
207
+ emit_dependencies(makefile, k, v)
208
+ end
209
+ end
210
+
211
+ def make_obj_rules(makefile, deps)
212
+ cmplr = cxx?(get_compiler) ? 'CXX' : 'CC'
213
+
214
+ @all_names.each do |n|
215
+ makefile.puts("$(OBJ_#{n}): $(#{n}_DEP)\n\t$(#{cmplr}) $(#{cmplr}FLAGS) -o $@ -c $(SRC_#{n})\n\n")
216
+ end
217
+ end
218
+
219
+ def make_out_rules(makefile, deps)
220
+ cmplr = cxx?(get_compiler) ? 'CXX' : 'CC'
221
+ if @workspace.mode == :bin
222
+ makefile.puts("out: $(OUT_OBJS)\n\t$(#{cmplr}) $(#{cmplr}FLAGS) -o $(TARGET) $(OUT_OBJS) $(LDFLAGS) $(LDLIBS)")
223
+ else
224
+ makefile.puts("out: $(OUT_OBJS)\n\t$(#{cmplr}) $(#{cmplr}FLAGS) -shared -o $(TARGET) $(OUT_OBJS) -fPIC $(LDFLAGS) $(LDLIBS)")
225
+ end
226
+ makefile.puts ''
227
+ makefile.puts("test: $(TESTS)")
228
+ makefile.puts ''
229
+ makefile.puts("all: out test")
230
+ end
231
+
232
+ def make_tests_rules(makefile, deps)
233
+ cmplr = cxx?(get_compiler) ? 'CXX' : 'CC'
234
+ @all_names.each do |n|
235
+ next unless n.start_with? 'TEST_'
236
+ filename = "#{@workspace.tests_short}/#{n.downcase}.#{@workspace.source_suffix}"
237
+ objs = ["$(OBJ_#{n})"] + extract_one_file_obj(filename, deps).map do |o|
238
+ "$(OBJ_#{File.basename(o, '.*').upcase})"
239
+ end
240
+
241
+ makefile.puts("$(#{n}): #{objs.join ' '}\n\t$(#{cmplr}) $(#{cmplr}FLAGS) -o $@ $^ $(LDFLAGS) $(LDLIBS)")
242
+ makefile.puts ''
243
+ end
244
+ end
245
+
246
+ def make_clean(makefile)
247
+ clean = <<~DOC
248
+ .PHONY: clean
249
+ clean:
250
+ \trm ./target/*
251
+ \trm ./obj/*.o
252
+ DOC
253
+ makefile.puts(clean)
254
+ end
255
+
256
+ def make_rules(makefile, deps)
257
+ make_dependencies makefile, deps
258
+ makefile.puts ''
259
+ make_out_rules makefile, deps
260
+ makefile.puts ''
261
+ make_obj_rules makefile, deps
262
+ make_tests_rules makefile, deps
263
+ makefile.puts ''
264
+ make_clean makefile
265
+ end
266
+ end
267
+
268
+ class WorkSpace
269
+ def make
270
+ config = ConfigReader.extract_flags "config.json"
271
+
272
+ deps = target_deps.merge tests_deps
273
+
274
+ makefile = CanoeMakefile.new self
275
+ makefile.configure config
276
+ makefile.make! deps
277
+ end
278
+ end
279
+ end