canoe 0.2.3 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d9763386960dfa876185c776041f832de8e6ff3fc050327fc8c616a02467137
4
- data.tar.gz: fde0dde4b2c9fc3a360a2bf91f2eb6828439cea7ea15d88790bae382abebbad7
3
+ metadata.gz: af31768c0b36d635c13ff31e41dc772dd9834ccd2d52e791f399053ebb3b9782
4
+ data.tar.gz: 135f95f9631bcc43c4817833ee9a6075b1d336239b9131c8f5f26d3f0006cd3b
5
5
  SHA512:
6
- metadata.gz: 9fd27c0889f66c239c0c305b7554418c3afe9b40bf7a1d6eaa3eb9e76e13247dcec8ddee48aeb58dfbfc8995f13ac05dd63de36837d680c87c6dbbf932b657b2
7
- data.tar.gz: 27655015444a90c232c398b8f7e02d4dfb454769d68086e4dcaa7039f41bae8cee33968cce105cf36ab92bde0b10464251e79b5443385f2f5598aa6ce593b054
6
+ metadata.gz: 9c82734822d59c1cd1a119dcc55f954f3d6b971457f44df22c8e9f6eceaa638c503816a1f05dc54019ea8ded2c70f1431e26cab0c057db3cd4ee2775dc12583c
7
+ data.tar.gz: 9b23406264a260e2b715018f5ad275c6a8b9293d712fd4d7623b03709fa5d93ee03cbc916472d9b24e9e6e1d4e2dd83e6781903ca7987febc4ec28a3e92e0ce5
data/bin/canoe CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # require_relative '../lib/canoe'
2
3
  require 'canoe'
3
4
 
4
5
  Canoe.new.parse ARGV
data/lib/canoe.rb CHANGED
@@ -1,23 +1,25 @@
1
- require_relative "workspace"
1
+ require_relative "workspace/workspace.rb"
2
2
  require_relative "cmd"
3
3
  require_relative "source_files"
4
4
 
5
5
  class Canoe
6
6
  def initialize
7
- options = ['new',
8
- 'add',
9
- 'build',
10
- 'generate',
11
- 'run',
12
- 'dep',
13
- 'clean',
14
- 'version',
15
- 'help',
16
- 'update']
7
+ options = ["new",
8
+ "add",
9
+ "build",
10
+ "generate",
11
+ "make",
12
+ "run",
13
+ "dep",
14
+ "clean",
15
+ "version",
16
+ "help",
17
+ "update",
18
+ "test"]
17
19
  @cmd = CmdParser.new options
18
20
  end
19
21
 
20
22
  def parse(args)
21
- @cmd.parse args
23
+ @cmd.parse args
22
24
  end
23
25
  end
data/lib/cmd.rb CHANGED
@@ -1,16 +1,20 @@
1
- require_relative "workspace"
1
+ require_relative "workspace/workspace"
2
2
  require_relative "err"
3
3
  require_relative "config_reader"
4
4
 
5
+ ##
6
+ # class CmdParser
7
+ # Parsing command arguments passed to canoe
5
8
  class CmdParser
6
9
  include Err
10
+
7
11
  def initialize(options)
8
- @options = options
12
+ @options = options
9
13
  end
10
14
 
11
15
  def parse(args)
12
- if args.size < 1
13
- abort_on_err "please give one command among #{@options.join(', ')}"
16
+ if args.size < 1
17
+ abort_on_err "please give one command among #{@options.join(", ")}"
14
18
  end
15
19
 
16
20
  unless @options.include?(args[0])
@@ -21,6 +25,7 @@ class CmdParser
21
25
  end
22
26
 
23
27
  private
28
+
24
29
  def get_current_workspace
25
30
  abort_on_err "not in a canoe workspace" unless File.exists? ".canoe"
26
31
  config = ConfigReader.extract_flags("config.json")
@@ -31,7 +36,7 @@ class CmdParser
31
36
  name = Dir.pwd.split("/")[-1]
32
37
  mode = File.exists?("src/main.#{src_sfx}") ? :bin : :lib
33
38
 
34
- Dir.chdir('..') do
39
+ Dir.chdir("..") do
35
40
  return WorkSpace.new(name, mode, src_sfx, hdr_sfx)
36
41
  end
37
42
  end
@@ -41,10 +46,10 @@ class CmdParser
41
46
 
42
47
  name, mode = nil, "bin"
43
48
  suffixes = ["cpp", "hpp"]
44
-
49
+
45
50
  args.each do |arg|
46
51
  case arg
47
- when '--bin', '--lib'
52
+ when "--bin", "--lib"
48
53
  mode = arg[2..]
49
54
  when /--suffix=(\w+)\:(\w+)/
50
55
  suffixes[0], suffixes[1] = $1, $2
@@ -52,7 +57,7 @@ class CmdParser
52
57
  name = arg unless name
53
58
  end
54
59
  end
55
-
60
+
56
61
  abort_on_err("please give a name to this project") unless name
57
62
  WorkSpace.new(name, mode.to_sym, suffixes[0], suffixes[1]).new
58
63
  end
@@ -64,7 +69,7 @@ class CmdParser
64
69
 
65
70
  get_current_workspace.add args
66
71
  end
67
-
72
+
68
73
  def parse_build(args)
69
74
  get_current_workspace.build args
70
75
  end
@@ -72,31 +77,35 @@ class CmdParser
72
77
  def parse_generate(args)
73
78
  get_current_workspace.generate
74
79
  end
75
-
80
+
76
81
  def parse_run(args)
77
82
  get_current_workspace.run args
78
83
  end
79
84
 
80
85
  def parse_dep(args)
81
- get_current_workspace.dep
86
+ get_current_workspace.dep
82
87
  end
83
-
88
+
84
89
  def parse_clean(args)
85
90
  get_current_workspace.clean
86
91
  end
87
92
 
93
+ def parse_test(args)
94
+ get_current_workspace.test args
95
+ end
96
+
88
97
  def parse_version(args)
89
98
  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
99
+ canoe v0.3.1
100
+ For features in this version, please visit https://github.com/Dicridon/canoe
101
+ Currently, canoe can do below:
102
+ - project creation
103
+ - project auto build and run (works like Cargo for Rust)
104
+ - project structure management
105
+ by XIONG Ziwei
106
+ VER
98
107
  end
99
-
108
+
100
109
  def parse_help(args)
101
110
  WorkSpace.help
102
111
  end
@@ -104,4 +113,8 @@ class CmdParser
104
113
  def parse_update(args)
105
114
  get_current_workspace.update
106
115
  end
116
+
117
+ def parse_make(args)
118
+ get_current_workspace.make
119
+ end
107
120
  end
data/lib/coloring.rb ADDED
@@ -0,0 +1,23 @@
1
+ ##
2
+ # gem Colorize is a great tool, but I don't want add dependencies to Canoe
3
+ class String
4
+ def self.define_coloring_methods
5
+ colors = {
6
+ 30 => :black,
7
+ 31 => :red,
8
+ 32 => :green,
9
+ 33 => :yellow,
10
+ 34 => :blue,
11
+ 35 => :magenta,
12
+ 36 => :cyan,
13
+ 37 => :white,
14
+ }
15
+ colors.each do |k, v|
16
+ define_method v do
17
+ "\033[#{k}m#{self}\033[0m"
18
+ end
19
+ end
20
+ end
21
+
22
+ define_coloring_methods
23
+ end
data/lib/compiler.rb CHANGED
@@ -1,27 +1,46 @@
1
+ ##
2
+ # class Compiler
3
+ # Storing compiler name in String and flags as an array
1
4
  class Compiler
2
5
  attr_reader :name, :flags
3
- def initialize(name, flgs)
6
+ ##
7
+ # @name: String
8
+ # @flgs: Array of String
9
+ def initialize(name, compiling_flags, linking_flags)
4
10
  @name = name
5
- @flags = flgs
11
+ @linking_flags = linking_flags
12
+ @compiling_flags = compiling_flags
6
13
  end
7
14
 
8
- def flags_as_str
9
- flags.join " "
15
+ def compiling_flags_as_str
16
+ @compiling_flags.join " "
10
17
  end
11
18
 
12
- def append_flag(flag)
13
- @flags << flag
19
+ def linking_flags_as_str
20
+ @linking_flags.join " "
21
+ end
22
+
23
+ def append_compiling_flag(flag)
24
+ @compiling_flags << flag
25
+ end
26
+
27
+ def append_linking_flag(flag)
28
+ @linking_flags << flag
14
29
  end
15
30
 
16
31
  def compile(src, out)
17
- puts "#{name} -o #{out} #{flags_as_str} -c #{src}"
18
- system "#{name} -o #{out} #{flags_as_str} -c #{src}"
32
+ puts "#{name} -o #{out} #{compiling_flags_as_str} -c #{src}"
33
+ system "#{name} -o #{out} #{compiling_flags_as_str} -c #{src}"
34
+ end
35
+
36
+ def link_executable(out, objs)
37
+ puts "#{name} -o #{out} #{objs.join(" ")} #{linking_flags_as_str}"
38
+ system "#{name} -o #{out} #{objs.join(" ")} #{linking_flags_as_str}"
19
39
  end
20
40
 
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(" ")}"
41
+ def link_shared(out, objs)
42
+ puts "#{name} -shared -o #{out}.so #{objs.join(" ")} #{linking_flags_as_str}"
43
+ system "#{name} -shared -o #{out}.so #{objs.join(" ")} #{linking_flags_as_str}"
25
44
  end
26
45
 
27
46
  def inspect
data/lib/config_reader.rb CHANGED
@@ -1,5 +1,8 @@
1
- require 'json'
1
+ require "json"
2
2
 
3
+ ##
4
+ # class ConfigReader
5
+ # Just read a json file
3
6
  class ConfigReader
4
7
  def self.extract_flags(file)
5
8
  abort_on_err("config file #{file} does not exsit") unless File.exists? file
data/lib/default_files.rb CHANGED
@@ -1,70 +1,98 @@
1
+ ##
2
+ # class DefaultFiles
3
+ # A singleton class to generate header and souce files.
4
+ # TODO: consider using class source_file.rb in Pareater
1
5
  class DefaultFiles
2
- def self.open_file_and_write(filename, content)
3
- File.open(filename, "w") {|f|
4
- f.write(content)
5
- }
6
- end
6
+ class << self
7
+ def open_file_and_write(filename, content)
8
+ File.open(filename, "w") do |f|
9
+ f.write(content)
10
+ end
11
+ end
7
12
 
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
13
+ def create_config(path, src_sfx = "cpp", hdr_sfx = "hpp")
14
+ open_file_and_write(
15
+ "#{path}/config.json",
16
+ <<~CONFIG
17
+ {
18
+ "compiler": "clang++",
19
+ "header-suffix": "#{hdr_sfx}",
20
+ "source-suffix": "#{src_sfx}",
21
+ "flags": {
22
+ "compile": {
23
+ "opt": "-O2",
24
+ "debug": "-g",
25
+ "std": "-std=c++17"
26
+ },
27
+ "link": {
28
+
29
+ }
30
+ }
31
+ }
32
+ CONFIG
33
+
34
+ )
35
+ end
25
36
 
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
+ def create_main(path, suffix = "cpp")
38
+ open_file_and_write(
39
+ "#{path}/main.#{suffix}",
40
+ <<~DOC
41
+ #include <iostream>
42
+ int main(int argc, char *argv[]) {
43
+ std::cout << "hello world!" << std::endl;
44
+ }
45
+ DOC
46
+
47
+ )
48
+ end
37
49
 
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
50
+ def create_lib_header(path, lib_name, suffix = "hpp")
51
+ open_file_and_write(
52
+ "#{path}/#{lib_name}.#{suffix}",
53
+ <<~DOC
54
+ #ifndef __#{lib_name.upcase}__
55
+ #define __#{lib_name.upcase}__
56
+
57
+ #endif
58
+ DOC
59
+
60
+ )
61
+ end
49
62
 
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
63
+ # def create_emacs_dir_local(path)
64
+ # open_file_and_write(
65
+ # "#{path}/.dir-locals.el",
66
+ # <<~DOC
67
+ # ((nil . ((company-clang-arguments . ("-I./src/components/"
68
+ # "-I./components/"))))
69
+ # (nil . ((company-c-headers-path-user . ("./src/components/"
70
+ # "./components/")))))
71
+ # DOC
72
+ # )
73
+ # end
58
74
 
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}__
75
+ def create_cpp(filename, src_sfx = "cpp", hdr_sfx = "hpp")
76
+ open_file_and_write(
77
+ "#{filename}.#{src_sfx}",
78
+ <<~DOC
79
+ #include "#{filename}.#{hdr_sfx}"
80
+ DOC
81
+
82
+ )
83
+ end
65
84
 
66
- #endif
67
- DOC
68
- )
85
+ def create_hpp(workspace, prefix, filename, hdr_sfx = "hpp")
86
+ open_file_and_write(
87
+ "#{filename}.#{hdr_sfx}",
88
+ <<~DOC
89
+ #ifndef __#{workspace.upcase}__#{prefix.upcase}__#{filename.upcase}__
90
+ #define __#{workspace.upcase}__#{prefix.upcase}__#{filename.upcase}__
91
+
92
+ #endif
93
+ DOC
94
+
95
+ )
96
+ end
69
97
  end
70
98
  end
data/lib/dependence.rb CHANGED
@@ -1,31 +1,54 @@
1
- require_relative 'source_files'
2
- require_relative 'err'
1
+ require_relative "source_files"
2
+ require_relative "err"
3
3
 
4
+ ##
5
+ # class DepAnalyzer
6
+ # This class is the key component of canoe, which offers file dependency analysis functionality.
7
+ # A DepAnalyzer takes a directory as input, sources files and corresponding header files in this
8
+ # directory should have same name, e.g. test.cpp and test.hpp.
9
+ # DepAnalyzer would read every source file and recursively process user header files included in this source file to
10
+ # find out all user header files this source file depends on.
11
+ # Based on dependencies built in previous stage, DepAnalyzer determines which files should be recompiled and return
12
+ # these files to caller.
13
+ #
14
+ # Dependencies could be written to a file to avoid wasting time parsing all files, Depanalyzer would read from
15
+ # this file to construct dependencies. But if sources files included new headers or included headers are revmoed,
16
+ # Depanalyzer should rebuild the whole dependencies.
4
17
  class DepAnalyzer
5
18
  include Err
6
19
  def self.read_from(filename)
7
20
  File.open(filename, "r") do |f|
8
21
  ret = Hash.new []
9
22
  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
23
+ entry = line.split(": ")
24
+ Err.abort_on_err("Bad .canoe.deps format, line #{i + 1}") unless entry.length == 2
12
25
  ret[entry[0]] = entry[1].split
13
- end
26
+ end
14
27
  ret
15
28
  end
16
29
  end
17
30
 
18
- def self.compiling_filter(deps, build_time, src_sfx='cpp', hdr_sfx='hpp')
19
- files = []
31
+ def self.compiling_filter(deps, build_time, src_sfx = "cpp", hdr_sfx = "hpp")
32
+ files = []
33
+ @processed = {}
34
+ @recompiles = {}
35
+ deps.keys.each do |k|
36
+ @processed[k] = false
37
+ @recompiles[k] = false
38
+ end
20
39
  deps.each do |k, v|
21
40
  next if k.end_with? ".#{hdr_sfx}"
22
41
  if should_recompile?(k, build_time)
23
42
  files << k
43
+ @processed[k] = true
44
+ @recompiles[k] = true
24
45
  next
25
46
  end
26
47
  v.each do |f|
27
48
  if mark(f, build_time, deps) || mark(f.sub(".#{hdr_sfx}", ".#{src_sfx}"), build_time, deps)
28
49
  files << k
50
+ @processed[k] = true
51
+ @recompiles[k] = true
29
52
  break
30
53
  end
31
54
  end
@@ -34,22 +57,36 @@ class DepAnalyzer
34
57
  end
35
58
 
36
59
  private
60
+
37
61
  def self.mark(file, build_time, deps)
62
+ ret = false
38
63
  return false unless File.exists? file
39
64
  if should_recompile?(file, build_time)
40
65
  return true
41
66
  else
42
67
  deps[file].each do |f|
43
- return true if mark(f, build_time, deps)
68
+ if @processed[f]
69
+ ret |= @recompiles[f]
70
+ next
71
+ end
72
+ @processed[f] = true
73
+ if mark(f, build_time, deps)
74
+ @recompiles[f] = true
75
+ return true
76
+ end
44
77
  end
45
78
  end
46
- false
79
+ ret
47
80
  end
48
81
 
49
- def self.should_recompile?(file, build_time)
82
+ def self.should_recompile?(file, build_time)
50
83
  judge = build_time
51
84
  if build_time == Time.new(0)
52
- objfile = "./obj/#{File.basename(file, ".*")}.o"
85
+ objfile = if file.start_with?("./src/components")
86
+ "./obj/" + file.delete_suffix(File.extname(file))["./src/components/".length..].gsub("/", "_") + ".o"
87
+ else
88
+ "./obj/#{File.basename(file, ".*")}.o"
89
+ end
53
90
  return true unless File.exists? objfile
54
91
  judge = File.mtime(objfile)
55
92
  end
@@ -57,7 +94,8 @@ class DepAnalyzer
57
94
  end
58
95
 
59
96
  public
60
- def initialize(dir, src_sfx='cpp', hdr_sfx='hpp')
97
+
98
+ def initialize(dir, src_sfx = "cpp", hdr_sfx = "hpp")
61
99
  @dir = dir
62
100
  @deps = Hash.new []
63
101
  @source_suffix = src_sfx
@@ -79,25 +117,26 @@ class DepAnalyzer
79
117
 
80
118
  def build_to_file(include_path, filename)
81
119
  build_dependence include_path
82
-
120
+
83
121
  File.open(filename, "w") do |f|
84
122
  @deps.each do |k, v|
85
123
  f.write "#{k}: #{v.join(" ")}\n"
86
124
  end
87
125
  end
88
-
126
+
89
127
  @deps
90
128
  end
91
129
 
92
130
  private
93
- def get_all_headers(include_path, file, suffix='hpp')
131
+
132
+ def get_all_headers(include_path, file, suffix = "hpp")
94
133
  File.open(file, "r") do |f|
95
134
  ret = []
96
135
  if file.end_with?(".#{@source_suffix}")
97
136
  header = file.sub(".#{@source_suffix}", ".#{@header_suffix}")
98
137
  ret += [header] if File.exists?(header)
99
138
  end
100
-
139
+
101
140
  f.each_line do |line|
102
141
  if mat = line.match(/include "(.+\.#{suffix})"/)
103
142
  include_path.each do |path|
@@ -106,7 +145,7 @@ class DepAnalyzer
106
145
  end
107
146
  end
108
147
  end
109
-
148
+
110
149
  ret.uniq
111
150
  end
112
151
  end