canoe 0.2.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
- data/bin/canoe +4 -0
- data/lib/canoe.rb +14 -0
- data/lib/cmd.rb +107 -0
- data/lib/compiler.rb +31 -0
- data/lib/config_reader.rb +8 -0
- data/lib/default_files.rb +70 -0
- data/lib/dependence.rb +113 -0
- data/lib/err.rb +19 -0
- data/lib/source_files.rb +42 -0
- data/lib/workspace.rb +272 -0
- metadata +56 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 78d6318d9757575b6e8f3f118e161ca06bb08655f26e7531e33c31d63f26aa18
|
|
4
|
+
data.tar.gz: 5cb8230a80e4bb2f03b8afb3dbebea90fccfd4b97bafa652502b2161e70ce374
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9501ebdd1d4ef09e6bfaede0c712044d665650b2a9d07dcfe490fe35afc378dae1d66429dc5919e217b48f4d3df9156611a4c496c64ebe3916870fc2da48453a
|
|
7
|
+
data.tar.gz: 848272aacd562ef7bc05ae50bd948a4221721b9432be46acb669e8ee6bb4f5a27e7e7ed57d5e6b64d83fce52be6a882955324f0c782b9e9252611ed29ba50cf8
|
data/bin/canoe
ADDED
data/lib/canoe.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require_relative "workspace"
|
|
2
|
+
require_relative "cmd"
|
|
3
|
+
require_relative "source_files"
|
|
4
|
+
|
|
5
|
+
class Canoe
|
|
6
|
+
def initialize
|
|
7
|
+
options = ["new", "build", "run", "clean", "help", "add", "generate", "deps", "version"]
|
|
8
|
+
@cmd = CmdParser.new options
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def parse(args)
|
|
12
|
+
@cmd.parse args
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/cmd.rb
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
require_relative "workspace"
|
|
2
|
+
require_relative "err"
|
|
3
|
+
require_relative "config_reader"
|
|
4
|
+
|
|
5
|
+
class CmdParser
|
|
6
|
+
include Err
|
|
7
|
+
def initialize(options)
|
|
8
|
+
@options = options
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def parse(args)
|
|
12
|
+
if args.size < 1
|
|
13
|
+
abort_on_err "please give one command among #{@options.join(', ')}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
unless @options.include?(args[0])
|
|
17
|
+
abort_on_err "unknown command #{args[0]}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
self.send "parse_#{args[0]}", args[1..]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
def get_current_workspace
|
|
25
|
+
abort_on_err "not in a canoe workspace" unless File.exists? ".canoe"
|
|
26
|
+
config = ConfigReader.extract_flags("config.json")
|
|
27
|
+
|
|
28
|
+
src_sfx = config["source-suffix"] ? config["source-suffix"] : "cpp"
|
|
29
|
+
hdr_sfx = config["header-suffix"] ? config["header-suffix"] : "hpp"
|
|
30
|
+
|
|
31
|
+
name = Dir.pwd.split("/")[-1]
|
|
32
|
+
mode = File.exists?("src/main.#{src_sfx}") ? :bin : :lib
|
|
33
|
+
|
|
34
|
+
Dir.chdir('..') do
|
|
35
|
+
return WorkSpace.new(name, mode, src_sfx, hdr_sfx)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def parse_new(args)
|
|
40
|
+
abort_on_err "not enough arguments to canoe new" if args.size < 1
|
|
41
|
+
|
|
42
|
+
name, mode = nil, "bin"
|
|
43
|
+
suffixes = ["cpp", "hpp"]
|
|
44
|
+
|
|
45
|
+
args.each do |arg|
|
|
46
|
+
case arg
|
|
47
|
+
when '--bin', '--lib'
|
|
48
|
+
mode = arg[2..]
|
|
49
|
+
when /--suffix=(\w+)\:(\w+)/
|
|
50
|
+
suffixes[0], suffixes[1] = $1, $2
|
|
51
|
+
else
|
|
52
|
+
name = arg unless name
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
abort_on_err("please give a name to this project") unless name
|
|
57
|
+
WorkSpace.new(name, mode.to_sym, suffixes[0], suffixes[1]).new
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def parse_add(args)
|
|
61
|
+
if args.size < 1
|
|
62
|
+
abort_on_err "it's not reasonable to add a component with no name given"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
get_current_workspace.add args
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def parse_build(args)
|
|
69
|
+
get_current_workspace.build args
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def parse_generate(args)
|
|
73
|
+
get_current_workspace.generate
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def parse_run(args)
|
|
77
|
+
get_current_workspace.run args
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def parse_dep(args)
|
|
81
|
+
get_current_workspace.dep
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def parse_clean(args)
|
|
85
|
+
get_current_workspace.clean
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def parse_version(args)
|
|
89
|
+
puts <<~VER
|
|
90
|
+
canoe v0.2.1
|
|
91
|
+
For features in this version, please visit https://github.com/Dicridon/canoe
|
|
92
|
+
Currently, canoe can do below:
|
|
93
|
+
- project creation
|
|
94
|
+
- project auto build and run (works like Cargo for Rust)
|
|
95
|
+
- project structure management
|
|
96
|
+
by XIONG Ziwei
|
|
97
|
+
VER
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def parse_help(args)
|
|
101
|
+
WorkSpace.help
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def parse_update(args)
|
|
105
|
+
get_current_workspace.update
|
|
106
|
+
end
|
|
107
|
+
end
|
data/lib/compiler.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class Compiler
|
|
2
|
+
attr_reader :name, :flags
|
|
3
|
+
def initialize(name, flgs)
|
|
4
|
+
@name = name
|
|
5
|
+
@flags = flgs
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def flags_as_str
|
|
9
|
+
flags.join " "
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def append_flag(flag)
|
|
13
|
+
@flags << flag
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def compile(src, out)
|
|
17
|
+
puts "#{name} -o #{out} #{flags_as_str} -c #{src}"
|
|
18
|
+
system "#{name} -o #{out} #{flags_as_str} -c #{src}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def link(out, objs)
|
|
22
|
+
libs = flags.select {|f| f.start_with?('-l')}
|
|
23
|
+
puts "#{name} -o #{out} #{objs.join(" ")} #{libs.join(" ")}"
|
|
24
|
+
system "#{name} -o #{out} #{objs.join(" ")} #{libs.join(" ")}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def inspect
|
|
28
|
+
puts "compiler name: #{name.inspect}"
|
|
29
|
+
puts "compiler flags: #{flags.inspect}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
class DefaultFiles
|
|
2
|
+
def self.open_file_and_write(filename, content)
|
|
3
|
+
File.open(filename, "w") {|f|
|
|
4
|
+
f.write(content)
|
|
5
|
+
}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.create_config(path, src_sfx='cpp', hdr_sfx='hpp')
|
|
9
|
+
open_file_and_write(
|
|
10
|
+
"#{path}/config.json",
|
|
11
|
+
<<~CONFIG
|
|
12
|
+
{
|
|
13
|
+
"compiler": "clang++",
|
|
14
|
+
"header-suffix": "#{hdr_sfx}",
|
|
15
|
+
"source-suffix": "#{src_sfx}",
|
|
16
|
+
"flags": {
|
|
17
|
+
"opt": "-O2",
|
|
18
|
+
"debug": "-g",
|
|
19
|
+
"std": "-std=c++17"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
CONFIG
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.create_main(path, suffix='cpp')
|
|
27
|
+
open_file_and_write(
|
|
28
|
+
"#{path}/main.#{suffix}",
|
|
29
|
+
<<~DOC
|
|
30
|
+
#include <iostream>
|
|
31
|
+
int main(int argc, char *argv[]) {
|
|
32
|
+
std::cout << "hello world!" << std::endl;
|
|
33
|
+
}
|
|
34
|
+
DOC
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.create_emacs_dir_local(path)
|
|
39
|
+
open_file_and_write(
|
|
40
|
+
"#{path}/.dir-locals.el",
|
|
41
|
+
<<~DOC
|
|
42
|
+
((nil . ((company-clang-arguments . ("-I./src/components/"
|
|
43
|
+
"-I./components/"))))
|
|
44
|
+
(nil . ((company-c-headers-path-user . ("./src/components/"
|
|
45
|
+
"./components/")))))
|
|
46
|
+
DOC
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.create_cpp(filename, src_sfx='cpp', hdr_sfx='hpp')
|
|
51
|
+
open_file_and_write(
|
|
52
|
+
"#{filename}.#{src_sfx}",
|
|
53
|
+
<<~DOC
|
|
54
|
+
#include "#{filename}.#{hdr_sfx}"
|
|
55
|
+
DOC
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.create_hpp(workspace, prefix, filename, hdr_sfx='hpp')
|
|
60
|
+
open_file_and_write(
|
|
61
|
+
"#{filename}.#{hdr_sfx}",
|
|
62
|
+
<<~DOC
|
|
63
|
+
#ifndef __#{workspace.upcase}__#{prefix.upcase}__#{filename.upcase}__
|
|
64
|
+
#define __#{workspace.upcase}__#{prefix.upcase}__#{filename.upcase}__
|
|
65
|
+
|
|
66
|
+
#endif
|
|
67
|
+
DOC
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/dependence.rb
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
require_relative 'source_files'
|
|
2
|
+
require_relative 'err'
|
|
3
|
+
|
|
4
|
+
class DepAnalyzer
|
|
5
|
+
include Err
|
|
6
|
+
def self.read_from(filename)
|
|
7
|
+
File.open(filename, "r") do |f|
|
|
8
|
+
ret = Hash.new []
|
|
9
|
+
f.each_with_index do |line, i|
|
|
10
|
+
entry = line.split(': ')
|
|
11
|
+
Err.abort_on_err("Bad .canoe.deps format, line #{i+1}") unless entry.length == 2
|
|
12
|
+
ret[entry[0]] = entry[1].split
|
|
13
|
+
end
|
|
14
|
+
ret
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.compiling_filter(deps, build_time, src_sfx='cpp', hdr_sfx='hpp')
|
|
19
|
+
files = []
|
|
20
|
+
deps.each do |k, v|
|
|
21
|
+
next if k.end_with? ".#{hdr_sfx}"
|
|
22
|
+
if should_recompile?(k, build_time)
|
|
23
|
+
files << k
|
|
24
|
+
next
|
|
25
|
+
end
|
|
26
|
+
v.each do |f|
|
|
27
|
+
if mark(f, build_time, deps) || mark(f.sub(".#{hdr_sfx}", ".#{src_sfx}"), build_time, deps)
|
|
28
|
+
files << k
|
|
29
|
+
break
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
files
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
def self.mark(file, build_time, deps)
|
|
38
|
+
return false unless File.exists? file
|
|
39
|
+
if should_recompile?(file, build_time)
|
|
40
|
+
return true
|
|
41
|
+
else
|
|
42
|
+
deps[file].each do |f|
|
|
43
|
+
return true if mark(f, build_time, deps)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
false
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.should_recompile?(file, build_time)
|
|
50
|
+
judge = build_time
|
|
51
|
+
if build_time == Time.new(0)
|
|
52
|
+
objfile = "./obj/#{File.basename(file, ".*")}.o"
|
|
53
|
+
return true unless File.exists? objfile
|
|
54
|
+
judge = File.mtime(objfile)
|
|
55
|
+
end
|
|
56
|
+
File.mtime(file) > judge
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
public
|
|
60
|
+
def initialize(dir, src_sfx='cpp', hdr_sfx='hpp')
|
|
61
|
+
@dir = dir
|
|
62
|
+
@deps = Hash.new []
|
|
63
|
+
@source_suffix = src_sfx
|
|
64
|
+
@header_suffix = hdr_sfx
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def build_dependence(include_path)
|
|
68
|
+
files = SourceFiles.get_all(@dir) do |f|
|
|
69
|
+
f.end_with?(".#{@source_suffix}") || f.end_with?(".#{@header_suffix}")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
@deps = Hash.new []
|
|
73
|
+
files.each do |fname|
|
|
74
|
+
@deps[fname] = get_all_headers include_path, fname, @header_suffix
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
@deps
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def build_to_file(include_path, filename)
|
|
81
|
+
build_dependence include_path
|
|
82
|
+
|
|
83
|
+
File.open(filename, "w") do |f|
|
|
84
|
+
@deps.each do |k, v|
|
|
85
|
+
f.write "#{k}: #{v.join(" ")}\n"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
@deps
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
def get_all_headers(include_path, file, suffix='hpp')
|
|
94
|
+
File.open(file, "r") do |f|
|
|
95
|
+
ret = []
|
|
96
|
+
if file.end_with?(".#{@source_suffix}")
|
|
97
|
+
header = file.sub(".#{@source_suffix}", ".#{@header_suffix}")
|
|
98
|
+
ret += [header] if File.exists?(header)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
f.each_line do |line|
|
|
102
|
+
if mat = line.match(/include "(.+\.#{suffix})"/)
|
|
103
|
+
include_path.each do |path|
|
|
104
|
+
dep = "#{path}/#{mat[1]}"
|
|
105
|
+
ret += [dep] if File.exists? dep
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
ret.uniq
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
data/lib/err.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Err
|
|
2
|
+
def warn_on_err(err)
|
|
3
|
+
puts <<~ERR
|
|
4
|
+
Warning:
|
|
5
|
+
#{err}
|
|
6
|
+
try 'canoe help' for more information
|
|
7
|
+
ERR
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def abort_on_err(err)
|
|
11
|
+
abort <<~ERR
|
|
12
|
+
Fatal:
|
|
13
|
+
#{err}
|
|
14
|
+
try 'canoe help' for more information
|
|
15
|
+
ERR
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module_function :warn_on_err, :abort_on_err
|
|
19
|
+
end
|
data/lib/source_files.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
class SourceFiles
|
|
2
|
+
class << self
|
|
3
|
+
def get_all(dir, &block)
|
|
4
|
+
@files = []
|
|
5
|
+
get_all_helper(dir, &block)
|
|
6
|
+
@files
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def get_in(dir, &block)
|
|
10
|
+
@files = []
|
|
11
|
+
Dir.each_child(dir) do |f|
|
|
12
|
+
file = "#{dir}/#{f}"
|
|
13
|
+
if File.file? file
|
|
14
|
+
if block_given?
|
|
15
|
+
@files << "#{file}" if yield(f)
|
|
16
|
+
else
|
|
17
|
+
@files << "#{file}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@files
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
def get_all_helper(dir, &block)
|
|
27
|
+
Dir.each_child(dir) do |f|
|
|
28
|
+
file = "#{dir}/#{f}"
|
|
29
|
+
if File.file? file
|
|
30
|
+
if block_given?
|
|
31
|
+
@files << "#{file}" if yield(f)
|
|
32
|
+
else
|
|
33
|
+
@files << "#{file}"
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
get_all_helper("#{file}", &block)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
data/lib/workspace.rb
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'open3'
|
|
3
|
+
require_relative 'source_files'
|
|
4
|
+
require_relative 'compiler'
|
|
5
|
+
require_relative 'config_reader'
|
|
6
|
+
require_relative 'default_files'
|
|
7
|
+
require_relative 'err'
|
|
8
|
+
require_relative 'dependence'
|
|
9
|
+
|
|
10
|
+
class WorkSpace
|
|
11
|
+
include Err
|
|
12
|
+
attr_reader :name, :cwd
|
|
13
|
+
def self.help
|
|
14
|
+
info = <<~INFO
|
|
15
|
+
canoe is a C/C++ project manager, inspired by Rust cargo.
|
|
16
|
+
usage:
|
|
17
|
+
canoe new tada: create a project named 'tada' in current directory
|
|
18
|
+
|
|
19
|
+
canoe build: compile current project (execute this command in project directory)
|
|
20
|
+
|
|
21
|
+
canoe generate: generate dependency relationship and store it in '.canoe.deps'
|
|
22
|
+
|
|
23
|
+
canoe run: compile and execute current project (execute this command in project directory)
|
|
24
|
+
|
|
25
|
+
canoe clean: remove all generated object files and binary files
|
|
26
|
+
|
|
27
|
+
canoe help: show this help message
|
|
28
|
+
|
|
29
|
+
canoe add tada: add a folder named tada under workspace/components,
|
|
30
|
+
two files tada.hpp and tada.cpp would be craeted and intialized
|
|
31
|
+
|
|
32
|
+
canoe verion: version information
|
|
33
|
+
|
|
34
|
+
new project_name [mode] [suffixes]:
|
|
35
|
+
create a new project with project_name.
|
|
36
|
+
In this project, four directories obj, src, target and third-party will be generated in project directory.
|
|
37
|
+
in src, directory 'components' will be generated if [mode] is '--lib', an extra main.cpp will be generated if [mode] is '--bin'
|
|
38
|
+
|
|
39
|
+
[mode]: --lib for a library and --bin for executable binaries
|
|
40
|
+
[suffixes]: should be in 'source_suffix:header_suffix" format, notice the ':' between two suffixes
|
|
41
|
+
|
|
42
|
+
generate:
|
|
43
|
+
generate dependence relationship for each file, this may accelarate
|
|
44
|
+
`canoe buid` command. It's recommanded to execute this command everytime
|
|
45
|
+
headers are added or removed from any file.
|
|
46
|
+
|
|
47
|
+
build [options]:
|
|
48
|
+
build current project, arguments in [options] will be passed to C++ compiler
|
|
49
|
+
|
|
50
|
+
run [options]:
|
|
51
|
+
build current project with no specific compilation flags, and run this project, passing [options] as command line arguments to the binary
|
|
52
|
+
|
|
53
|
+
clean:
|
|
54
|
+
remove all generated object files and binary files
|
|
55
|
+
|
|
56
|
+
help:
|
|
57
|
+
show this help message
|
|
58
|
+
|
|
59
|
+
verion:
|
|
60
|
+
display version information
|
|
61
|
+
|
|
62
|
+
dep:
|
|
63
|
+
display file dependencies in a better readable way
|
|
64
|
+
|
|
65
|
+
@author: written by XIONG Ziwei, ICT, CAS
|
|
66
|
+
@contact: noahxiong@outlook.com
|
|
67
|
+
INFO
|
|
68
|
+
puts info
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def initialize(name, mode, src_suffix='cpp', hdr_suffix='hpp')
|
|
73
|
+
@name = name
|
|
74
|
+
@compiler = Compiler.new 'clang++', '-Isrc/components'
|
|
75
|
+
@cwd = Dir.new(Dir.pwd)
|
|
76
|
+
@workspace = "#{Dir.pwd}/#{@name}"
|
|
77
|
+
@src = "#{@workspace}/src"
|
|
78
|
+
@components = "#{@src}/components"
|
|
79
|
+
@obj = "#{@workspace}/obj"
|
|
80
|
+
@third = "#{@workspace}/third-party"
|
|
81
|
+
@target = "#{@workspace}/target"
|
|
82
|
+
@mode = mode
|
|
83
|
+
@deps = '.canoe.deps'
|
|
84
|
+
|
|
85
|
+
@src_prefix = './src/'
|
|
86
|
+
@components_prefix = './src/components/'
|
|
87
|
+
@obj_prefix = './obj/'
|
|
88
|
+
|
|
89
|
+
@source_suffix = src_suffix
|
|
90
|
+
@header_suffix = hdr_suffix
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def new
|
|
94
|
+
Dir.mkdir(@name)
|
|
95
|
+
Dir.mkdir(@src)
|
|
96
|
+
Dir.mkdir(@components)
|
|
97
|
+
Dir.mkdir("#{@workspace}/obj")
|
|
98
|
+
DefaultFiles.create_main(@src, @source_suffix) if @mode == :bin
|
|
99
|
+
File.new("#{@workspace}/.canoe", "w")
|
|
100
|
+
DefaultFiles.create_config @workspace, @source_suffix, @header_suffix
|
|
101
|
+
DefaultFiles.create_emacs_dir_local @workspace
|
|
102
|
+
|
|
103
|
+
Dir.mkdir(@third)
|
|
104
|
+
Dir.mkdir(@target)
|
|
105
|
+
puts "workspace #{@workspace} is created"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# args are commandline parameters passed to `canoe build`
|
|
109
|
+
def build(args)
|
|
110
|
+
deps = File.exist?(@deps) ?
|
|
111
|
+
DepAnalyzer.read_from(@deps) :
|
|
112
|
+
DepAnalyzer.new('./src').build_to_file(['./src', './src/components'], @deps)
|
|
113
|
+
target = "./target/#{@name}"
|
|
114
|
+
build_time = File.exist?(target) ? File.mtime(target) : Time.new(0)
|
|
115
|
+
files = DepAnalyzer.compiling_filter(deps, build_time, @source_suffix, @header_suffix)
|
|
116
|
+
|
|
117
|
+
if files.empty?
|
|
118
|
+
puts "nothing to do, all up to date"
|
|
119
|
+
return
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
self.send "build_#{@mode.to_s}", files, args
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def generate
|
|
126
|
+
DepAnalyzer.new('./src', @source_suffix, @header_suffix)
|
|
127
|
+
.build_to_file ['./src', './src/components'], @deps
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def update
|
|
131
|
+
generate
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def clean
|
|
135
|
+
self.send "clean_#{@mode.to_s}"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def run(args)
|
|
139
|
+
build []
|
|
140
|
+
args = args.join " "
|
|
141
|
+
puts "./target/#{@name} #{args}"
|
|
142
|
+
exec "./target/#{@name} #{args}"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def add(args)
|
|
146
|
+
args.each do |i|
|
|
147
|
+
dir = @components
|
|
148
|
+
filenames = i.split("/")
|
|
149
|
+
prefix = []
|
|
150
|
+
filenames.each do |filename|
|
|
151
|
+
dir += "/#{filename}"
|
|
152
|
+
prefix << filename
|
|
153
|
+
unless Dir.exist? dir
|
|
154
|
+
FileUtils.mkdir dir
|
|
155
|
+
Dir.chdir(dir) do
|
|
156
|
+
puts "created " + Dir.pwd
|
|
157
|
+
create_working_files prefix.join('__'), filename
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def dep
|
|
165
|
+
deps = DepAnalyzer.read_from(@deps) if File.exist?(@deps)
|
|
166
|
+
deps.each do |k, v|
|
|
167
|
+
unless v.empty?
|
|
168
|
+
puts "#{k} depends on: "
|
|
169
|
+
v.each {|f| puts " #{f}"}
|
|
170
|
+
puts ""
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
private
|
|
176
|
+
def create_working_files(prefix, filename)
|
|
177
|
+
DefaultFiles.create_cpp filename, @source_suffix, @header_suffix
|
|
178
|
+
DefaultFiles.create_hpp @name, prefix, filename, @header_suffix
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def build_compiler_from_config(args)
|
|
182
|
+
Dir.chdir(@workspace) do
|
|
183
|
+
flags = ConfigReader.extract_flags "config.json"
|
|
184
|
+
compiler_name = flags['compiler'] ? flags['compiler'] : "clang++"
|
|
185
|
+
abort_on_err "compiler #{compiler_name} not found" unless File.exists?("/usr/bin/#{compiler_name}")
|
|
186
|
+
compiler_flags = ['-Isrc/components'] + args
|
|
187
|
+
|
|
188
|
+
if opts = flags['flags']
|
|
189
|
+
opts.each do |k, v|
|
|
190
|
+
case v
|
|
191
|
+
when String
|
|
192
|
+
compiler_flags << v
|
|
193
|
+
when Array
|
|
194
|
+
v.each do |o|
|
|
195
|
+
compiler_flags << o
|
|
196
|
+
end
|
|
197
|
+
else
|
|
198
|
+
abort_on_err "unknown options in config.json, #{v}"
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
@compiler = Compiler.new compiler_name, compiler_flags
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def compile(f, o)
|
|
208
|
+
@compiler.compile f, o
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def link(odir, objs)
|
|
212
|
+
status = system "#{@compiler} -o #{odir}/#{@name} #{objs.join(" ")}"
|
|
213
|
+
unless status
|
|
214
|
+
puts "compilation failed"
|
|
215
|
+
return
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
@compiler.link "#{odir}/#{@name}", objs
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def build_bin(files, args)
|
|
222
|
+
build_compiler_from_config args
|
|
223
|
+
comps = files.select {|f| f.start_with? @components_prefix}
|
|
224
|
+
srcs = files - comps
|
|
225
|
+
|
|
226
|
+
srcs.each do |f|
|
|
227
|
+
puts "compiling #{f}"
|
|
228
|
+
fname = f.split("/")[-1]
|
|
229
|
+
o = @obj_prefix + fname.delete_suffix(File.extname(fname)) + '.o'
|
|
230
|
+
compile f, o
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
comps.each do |f|
|
|
234
|
+
puts "compiling #{f}"
|
|
235
|
+
o = @obj_prefix + f.delete_suffix(File.extname(f))[@components_prefix.length..]
|
|
236
|
+
.gsub('/', '_') + '.o'
|
|
237
|
+
compile f, o
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
link('./target', Dir.glob("obj/*.o")) unless files.empty?
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def build_lib
|
|
244
|
+
puts "build a lib"
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def clean_obj
|
|
248
|
+
puts "rm -f ./obj/*.o"
|
|
249
|
+
system "rm -f ./obj/*.o"
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def clean_target
|
|
253
|
+
puts "rm -f ./target/*"
|
|
254
|
+
system "rm -f ./target/*"
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def clean_bin
|
|
258
|
+
clean_obj
|
|
259
|
+
clean_target
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def clean_lib
|
|
263
|
+
clean_obj
|
|
264
|
+
clean_target
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
public
|
|
268
|
+
def inspect
|
|
269
|
+
puts "name is #{@name}"
|
|
270
|
+
puts "name is #{@workspace}"
|
|
271
|
+
end
|
|
272
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: canoe
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- XIONG Ziwei
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2020-06-23 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: |
|
|
14
|
+
Tired of writing Makefile, CMakeList and even SConstruct? Let Canoe help you wipe them out.
|
|
15
|
+
Similar to Cargo for Rust, Canoe offers commands such as new, build, run, etc. to help you generate a C/C++ project and build it automatically.
|
|
16
|
+
No more Makefiles, Canoe would analyze dependencies and build like our old friend make if you follow a few conventions over file names
|
|
17
|
+
email: noahxiong@outlook.com
|
|
18
|
+
executables:
|
|
19
|
+
- canoe
|
|
20
|
+
extensions: []
|
|
21
|
+
extra_rdoc_files: []
|
|
22
|
+
files:
|
|
23
|
+
- bin/canoe
|
|
24
|
+
- lib/canoe.rb
|
|
25
|
+
- lib/cmd.rb
|
|
26
|
+
- lib/compiler.rb
|
|
27
|
+
- lib/config_reader.rb
|
|
28
|
+
- lib/default_files.rb
|
|
29
|
+
- lib/dependence.rb
|
|
30
|
+
- lib/err.rb
|
|
31
|
+
- lib/source_files.rb
|
|
32
|
+
- lib/workspace.rb
|
|
33
|
+
homepage: https://github.com/Dicridon/canoe
|
|
34
|
+
licenses:
|
|
35
|
+
- MIT
|
|
36
|
+
metadata: {}
|
|
37
|
+
post_install_message:
|
|
38
|
+
rdoc_options: []
|
|
39
|
+
require_paths:
|
|
40
|
+
- lib
|
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
47
|
+
requirements:
|
|
48
|
+
- - ">="
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: '0'
|
|
51
|
+
requirements: []
|
|
52
|
+
rubygems_version: 3.1.2
|
|
53
|
+
signing_key:
|
|
54
|
+
specification_version: 4
|
|
55
|
+
summary: a C/C++ project management and build tool
|
|
56
|
+
test_files: []
|