canoe 0.3.1.1 → 0.3.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.
data/lib/dependence.rb CHANGED
@@ -1,6 +1,5 @@
1
- require_relative "source_files"
2
- require_relative "err"
3
-
1
+ require_relative 'source_files'
2
+ require_relative 'util'
4
3
  ##
5
4
  # class DepAnalyzer
6
5
  # This class is the key component of canoe, which offers file dependency analysis functionality.
@@ -14,139 +13,140 @@ require_relative "err"
14
13
  # Dependencies could be written to a file to avoid wasting time parsing all files, Depanalyzer would read from
15
14
  # this file to construct dependencies. But if sources files included new headers or included headers are revmoed,
16
15
  # Depanalyzer should rebuild the whole dependencies.
17
- class DepAnalyzer
18
- include Err
19
- def self.read_from(filename)
20
- File.open(filename, "r") do |f|
21
- ret = Hash.new []
22
- f.each_with_index do |line, i|
23
- entry = line.split(": ")
24
- Err.abort_on_err("Bad .canoe.deps format, line #{i + 1}") unless entry.length == 2
25
- ret[entry[0]] = entry[1].split
16
+ module Canoe
17
+ class DepAnalyzer
18
+ include Err
19
+ include SystemCommand
20
+
21
+ class << self
22
+ include WorkSpaceUtil
23
+ def read_from(filename)
24
+ File.open(filename, 'r') do |f|
25
+ ret = Hash.new []
26
+ f.each_with_index do |line, i|
27
+ entry = line.split(': ')
28
+ abort_on_err("Bad .canoe.deps format, line #{i + 1}") unless entry.length == 2
29
+ ret[entry[0]] = entry[1].split
30
+ end
31
+ ret
32
+ end
26
33
  end
27
- ret
28
- end
29
- end
30
34
 
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
39
- deps.each do |k, v|
40
- next if k.end_with? ".#{hdr_sfx}"
41
- if should_recompile?(k, build_time)
42
- files << k
43
- @processed[k] = true
44
- @recompiles[k] = true
45
- next
46
- end
47
- v.each do |f|
48
- if mark(f, build_time, deps) || mark(f.sub(".#{hdr_sfx}", ".#{src_sfx}"), build_time, deps)
49
- files << k
50
- @processed[k] = true
51
- @recompiles[k] = true
52
- break
35
+ def compiling_filter(deps, build_time, src_sfx = 'cpp', hdr_sfx = 'hpp')
36
+ files = []
37
+ @processed = {}
38
+ @recompiles = {}
39
+ deps.each_key do |k|
40
+ @processed[k] = false
41
+ @recompiles[k] = false
53
42
  end
54
- end
55
- end
56
- files
57
- end
43
+ deps.each do |k, v|
44
+ next if k.end_with? ".#{hdr_sfx}"
45
+
46
+ if should_recompile?(k, build_time)
47
+ files << k
48
+ @processed[k] = true
49
+ @recompiles[k] = true
50
+ next
51
+ end
52
+ v.each do |f|
53
+ next unless mark(f, build_time, deps) || mark(f.sub(".#{hdr_sfx}", ".#{src_sfx}"), build_time, deps)
58
54
 
59
- private
60
-
61
- def self.mark(file, build_time, deps)
62
- ret = false
63
- return false unless File.exists? file
64
- if should_recompile?(file, build_time)
65
- return true
66
- else
67
- deps[file].each do |f|
68
- if @processed[f]
69
- ret |= @recompiles[f]
70
- next
55
+ files << k
56
+ @processed[k] = true
57
+ @recompiles[k] = true
58
+ break
59
+ end
71
60
  end
72
- @processed[f] = true
73
- if mark(f, build_time, deps)
74
- @recompiles[f] = true
75
- return true
61
+ files
62
+ end
63
+
64
+ private
65
+
66
+ def mark(file, build_time, deps)
67
+ ret = false
68
+ return false unless File.exist? file
69
+ return true if should_recompile?(file, build_time)
70
+
71
+ deps[file].each do |f|
72
+ if @processed[f]
73
+ ret |= @recompiles[f]
74
+ next
75
+ end
76
+ @processed[f] = true
77
+ if mark(f, build_time, deps)
78
+ @recompiles[f] = true
79
+ return true
80
+ end
76
81
  end
82
+ ret
77
83
  end
78
- end
79
- ret
80
- end
81
84
 
82
- def self.should_recompile?(file, build_time)
83
- judge = build_time
84
- if build_time == Time.new(0)
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"
85
+ def should_recompile?(file, build_time)
86
+ judge = build_time
87
+ if build_time == Time.new(0)
88
+ objfile = file_to_obj(file)
89
+ return true unless File.exist? objfile
90
+
91
+ judge = File.mtime(objfile)
89
92
  end
90
- return true unless File.exists? objfile
91
- judge = File.mtime(objfile)
93
+ File.mtime(file) > judge
94
+ end
92
95
  end
93
- File.mtime(file) > judge
94
- end
95
96
 
96
- public
97
+ def initialize(dir, src_sfx = 'cpp', hdr_sfx = 'hpp')
98
+ @dir = dir
99
+ @deps = Hash.new []
100
+ @source_suffix = src_sfx
101
+ @header_suffix = hdr_sfx
102
+ end
97
103
 
98
- def initialize(dir, src_sfx = "cpp", hdr_sfx = "hpp")
99
- @dir = dir
100
- @deps = Hash.new []
101
- @source_suffix = src_sfx
102
- @header_suffix = hdr_sfx
103
- end
104
+ def build_dependence(include_path)
105
+ files = SourceFiles.get_all(@dir) do |f|
106
+ f.end_with?(".#{@source_suffix}") || f.end_with?(".#{@header_suffix}")
107
+ end
104
108
 
105
- def build_dependence(include_path)
106
- files = SourceFiles.get_all(@dir) do |f|
107
- f.end_with?(".#{@source_suffix}") || f.end_with?(".#{@header_suffix}")
108
- end
109
+ @deps = Hash.new []
110
+ files.each do |fname|
111
+ @deps[fname] = get_all_headers include_path, fname, @header_suffix
112
+ end
109
113
 
110
- @deps = Hash.new []
111
- files.each do |fname|
112
- @deps[fname] = get_all_headers include_path, fname, @header_suffix
114
+ @deps
113
115
  end
114
116
 
115
- @deps
116
- end
117
+ def build_to_file(include_path, filename)
118
+ build_dependence include_path
117
119
 
118
- def build_to_file(include_path, filename)
119
- build_dependence include_path
120
-
121
- File.open(filename, "w") do |f|
122
- @deps.each do |k, v|
123
- f.write "#{k}: #{v.join(" ")}\n"
120
+ File.open(filename, 'w') do |f|
121
+ @deps.each do |k, v|
122
+ f.write "#{k}: #{v.join(' ')}\n"
123
+ end
124
124
  end
125
- end
126
125
 
127
- @deps
128
- end
126
+ @deps
127
+ end
129
128
 
130
- private
129
+ private
131
130
 
132
- def get_all_headers(include_path, file, suffix = "hpp")
133
- File.open(file, "r") do |f|
134
- ret = []
135
- if file.end_with?(".#{@source_suffix}")
136
- header = file.sub(".#{@source_suffix}", ".#{@header_suffix}")
137
- ret += [header] if File.exists?(header)
138
- end
131
+ def get_all_headers(include_path, file, suffix = 'hpp')
132
+ File.open(file, 'r') do |f|
133
+ ret = []
134
+ if file.end_with?(".#{@source_suffix}")
135
+ header = file.sub(".#{@source_suffix}", ".#{@header_suffix}")
136
+ ret += [header] if File.exist?(header)
137
+ end
139
138
 
140
- f.each_line do |line|
141
- if mat = line.match(/include "(.+\.#{suffix})"/)
142
- include_path.each do |path|
143
- dep = "#{path}/#{mat[1]}"
144
- ret += [dep] if File.exists? dep
139
+ f.each_line do |line|
140
+ if (mat = line.match(/include "(.+\.#{suffix})"/))
141
+ include_path.each do |path|
142
+ dep = "#{path}/#{mat[1]}"
143
+ ret += [dep] if File.exist? dep
144
+ end
145
145
  end
146
146
  end
147
- end
148
147
 
149
- ret.uniq
148
+ ret.uniq
149
+ end
150
150
  end
151
151
  end
152
152
  end
data/lib/source_files.rb CHANGED
@@ -9,15 +9,15 @@ class SourceFiles
9
9
  @files
10
10
  end
11
11
 
12
- def get_in(dir, &block)
12
+ def get_in(dir)
13
13
  @files = []
14
14
  Dir.each_child(dir) do |f|
15
15
  file = "#{dir}/#{f}"
16
16
  if File.file? file
17
17
  if block_given?
18
- @files << "#{file}" if yield(f)
18
+ @files << file.to_s if yield(f)
19
19
  else
20
- @files << "#{file}"
20
+ @files << file.to_s
21
21
  end
22
22
  end
23
23
  end
data/lib/util.rb ADDED
@@ -0,0 +1,81 @@
1
+ require_relative 'coloring'
2
+
3
+ module Canoe
4
+ class Stepper
5
+ def initialize(total, togo)
6
+ @total = total.to_f
7
+ @togo = togo.to_f
8
+ end
9
+
10
+ def progress_as_str
11
+ progress = ((@total - @togo) / @total).round(2) * 100
12
+ "[#{progress.to_i}%%]"
13
+ end
14
+
15
+ def step
16
+ @togo -= 1 if @togo.positive?
17
+ end
18
+ end
19
+
20
+ ##
21
+ # wrapping workspace related functionality to expose to other modules
22
+ module WorkSpaceUtil
23
+ def get_current_workspace
24
+ abort_on_err 'not in a canoe workspace' unless File.exist? '.canoe'
25
+ config = ConfigReader.extract_flags('config.json')
26
+
27
+ src_sfx = config['source-suffix'] || 'cpp'
28
+ hdr_sfx = config['header-suffix'] || 'hpp'
29
+
30
+ name = Dir.pwd.split('/')[-1]
31
+ mode = File.exist?("src/main.#{src_sfx}") ? :bin : :lib
32
+
33
+ WorkSpace.new(name, mode, src_sfx, hdr_sfx)
34
+ end
35
+
36
+ def src_to_obj(src)
37
+ get_current_workspace.src_to_obj(src)
38
+ end
39
+
40
+ def comp_to_obj(comp)
41
+ get_current_workspace.comp_to_obj(comp)
42
+ end
43
+
44
+ def file_to_obj(file)
45
+ get_current_workspace.file_to_obj(file)
46
+ end
47
+
48
+ def extract_one_file(file, deps)
49
+ get_current_workspace.extract_one_file(file, deps)
50
+ end
51
+
52
+ def extract_one_file_obj(file, deps)
53
+ get_current_workspace.extract_one_file_obj(file, deps)
54
+ end
55
+ end
56
+
57
+ module SystemCommand
58
+ def issue_command(cmd_str)
59
+ puts cmd_str
60
+ system cmd_str
61
+ end
62
+ end
63
+
64
+ module Err
65
+ def warn_on_err(err)
66
+ puts <<~ERR
67
+ #{'Waring: '.yellow}
68
+ #{err}
69
+ try 'canoe help' for more information
70
+ ERR
71
+ end
72
+
73
+ def abort_on_err(err)
74
+ abort <<~ERR
75
+ #{'Fatal: '.red}
76
+ #{err}
77
+ try 'canoe help' for more information
78
+ ERR
79
+ end
80
+ end
81
+ end
data/lib/workspace/add.rb CHANGED
@@ -1,27 +1,29 @@
1
- class WorkSpace
2
- def add(args)
3
- args.each do |i|
4
- dir = @components
5
- filenames = i.split("/")
6
- prefix = []
7
- filenames.each do |filename|
8
- dir += "/#{filename}"
9
- prefix << filename
10
- unless Dir.exist? dir
1
+ module Canoe
2
+ class WorkSpace
3
+ def add(args)
4
+ args.each do |i|
5
+ dir = @components
6
+ filenames = i.split '/'
7
+ prefix = []
8
+ filenames.each do |filename|
9
+ dir += "/#{filename}"
10
+ prefix << filename
11
+ next if Dir.exist? dir
12
+
11
13
  FileUtils.mkdir dir
12
14
  Dir.chdir(dir) do
13
- puts "created " + Dir.pwd.blue
14
- create_working_files prefix.join("__"), filename
15
+ puts "created + #{Dir.pwd.blue}"
16
+ create_working_files prefix.join('__'), filename
15
17
  end
16
18
  end
17
19
  end
18
20
  end
19
- end
20
21
 
21
- private
22
+ private
22
23
 
23
- def create_working_files(prefix, filename)
24
- DefaultFiles.create_cpp filename, @source_suffix, @header_suffix
25
- DefaultFiles.create_hpp @name, prefix, filename, @header_suffix
24
+ def create_working_files(prefix, filename)
25
+ DefaultFiles.create_cpp filename, @source_suffix, @header_suffix
26
+ DefaultFiles.create_hpp @name, prefix, filename, @header_suffix
27
+ end
26
28
  end
27
29
  end
@@ -1,39 +1,55 @@
1
- class WorkSpace
2
- # args are commandline parameters passed to `canoe build`,
3
- # could be 'all', 'test', 'target' or empty
4
- def build(args)
5
- case args[0]
6
- when "all"
7
- build_all
8
- when "test"
9
- build_test
10
- else
11
- build_target
1
+ module Canoe
2
+ class WorkSpace
3
+ def src_to_obj(src)
4
+ @obj_prefix + File.basename(src, ".*") + ".o"
12
5
  end
13
- end
14
6
 
15
- private
7
+ def comp_to_obj(comp)
8
+ @obj_prefix + comp.delete_suffix(File.extname(comp))[@components_prefix.length..].gsub("/", "_") + ".o"
9
+ end
16
10
 
17
- def build_flags(flags, config)
18
- config.values.each do |v|
19
- case v
20
- when String
21
- flags << v
22
- when Array
23
- v.each do |o|
24
- flags << o
25
- end
11
+ # the if else order is important because tests are regarded as sources
12
+ def file_to_obj(file)
13
+ if file.start_with?(@components_prefix)
14
+ comp_to_obj file
26
15
  else
27
- abort_on_err "unknown options in config.json, #{v}"
16
+ src_to_obj file
17
+ end
18
+ end
19
+
20
+ # args are commandline parameters passed to `canoe build`,
21
+ # could be 'all', 'test', 'target' or empty
22
+ def build(args)
23
+ options = {[] => 'target', ['all'] => 'all', ['test'] => 'test'}
24
+ if options.include?(args)
25
+ send "build_#{options[args]}"
26
+ else
27
+ abort_on_err "Unkown subcommand #{args.join(" ").red}"
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def build_flags(flags, config)
34
+ config.values.each do |v|
35
+ case v
36
+ when String
37
+ flags << v
38
+ when Array
39
+ v.each do |o|
40
+ flags << o
41
+ end
42
+ else
43
+ abort_on_err "unknown options in config.json, #{v}"
44
+ end
28
45
  end
29
46
  end
30
- end
31
47
 
32
- def build_compiler_from_config
33
- Dir.chdir(@workspace) do
48
+ def build_compiler_from_config
34
49
  flags = ConfigReader.extract_flags "config.json"
35
50
  compiler_name = flags["compiler"] ? flags["compiler"] : "clang++"
36
- abort_on_err "compiler #{compiler_name} not found" unless File.exists?("/usr/bin/#{compiler_name}")
51
+
52
+ abort_on_err "compiler #{compiler_name} not found" unless system "which #{compiler_name} > /dev/null"
37
53
  compiler_flags = ["-Isrc/components"]
38
54
  linker_flags = []
39
55
 
@@ -43,89 +59,92 @@ class WorkSpace
43
59
 
44
60
  @compiler = Compiler.new compiler_name, compiler_flags, linker_flags
45
61
  end
46
- end
47
62
 
48
- def compile(f, o)
49
- @compiler.compile f, o
50
- end
63
+ def compile(f, o)
64
+ @compiler.compile f, o
65
+ end
51
66
 
52
- def link_exectutable(odir, objs)
53
- puts "#{"[100%]".green} linking"
54
- @compiler.link_executable "#{odir}/#{@name}", objs
55
- end
67
+ def link_exectutable(odir, objs)
68
+ puts "#{"[100%]".green} linking"
69
+ @compiler.link_executable "#{odir}/#{@name}", objs
70
+ end
56
71
 
57
- def link_shared(odir, objs)
58
- puts "#{"[100%]".green} linking"
59
- @compiler.link_shared "#{odir}/lib#{@name}", objs
60
- end
72
+ def link_shared(odir, objs)
73
+ puts "#{"[100%]".green} linking"
74
+ @compiler.link_shared "#{odir}/lib#{@name}", objs
75
+ end
61
76
 
62
- def build_bin(files)
63
- # return if files.empty?
64
- build_compiler_from_config
65
- if build_common(files) && link_exectutable("./target", Dir.glob("obj/*.o"))
66
- puts "BUILDING SUCCEEDED".green
67
- else
68
- puts "building FAILED".red
77
+ def build_bin(files)
78
+ if build_common(files) &&
79
+ link_exectutable(@target_short, Dir.glob("obj/*.o").reject { |f| f.start_with? 'obj/test_' })
80
+ puts "BUILDING SUCCEEDED".green
81
+ return true
82
+ else
83
+ puts "building target FAILED".red
84
+ return false
85
+ end
69
86
  end
70
- end
71
87
 
72
- def build_lib(files)
73
- # return if files.empty?
74
- build_compiler_from_config
75
- @compiler.append_compiling_flag "-fPIC"
76
- if build_common(files) && link_shared("./target", Dir.glob("obj/*.o"))
77
- puts "BUILDING SUCCEEDED".green
78
- else
79
- puts "building FAILED".red
88
+ def build_lib(files)
89
+ @compiler.append_compiling_flag "-fPIC"
90
+ if build_common(files) &&
91
+ link_shared(@target_short, Dir.glob("obj/*.o").reject { |f| f.start_with? 'obj/test_'})
92
+ puts "BUILDING SUCCEEDED".green
93
+ else
94
+ puts "building target FAILED".red
95
+ end
80
96
  end
81
- end
82
97
 
83
- def build_common(files)
84
- all = SourceFiles.get_all("./src") { |f| f.end_with? @source_suffix }
85
- total = all.size.to_f
86
- compiled = total - files.size
87
- comps = files.select { |f| f.start_with? @components_prefix }
88
- srcs = files - comps
89
- flag = true
90
-
91
- srcs.each do |f|
92
- progress = (compiled / total).round(2) * 100
93
- printf "[#{progress.to_i}%%]".green + " compiling #{f}: "
94
- fname = f.split("/")[-1]
95
- o = @obj_prefix + File.basename(fname, ".*") + ".o"
96
- flag = false unless compile f, o
97
- compiled += 1
98
- end
99
-
100
- comps.each do |f|
101
- progress = (compiled / total).round(2) * 100
102
- printf "[#{progress.to_i}%%]".green + " compiling #{f}: "
103
- o = @obj_prefix + f.delete_suffix(File.extname(f))[@components_prefix.length..]
104
- .gsub("/", "_") + ".o"
105
- flag = false unless compile f, o
106
- compiled += 1
107
- end
108
- flag
109
- end
98
+ def build_common(files)
99
+ all = SourceFiles.get_all(@src_short) { |f| f.end_with? @source_suffix }
100
+ stepper = Stepper.new all.size, files.size
101
+ flag = true
102
+
103
+ files.each do |f|
104
+ progress = stepper.progress_as_str.green
105
+ printf "#{progress.green} compiling #{f.yellow}: "
106
+ o = file_to_obj(f)
107
+ flag = false unless compile f, o
108
+ stepper.step
109
+ end
110
+ flag
111
+ end
110
112
 
111
- def build_all
112
- build_target
113
- build_test
114
- end
113
+ def build_all
114
+ build_target
115
+ build_test
116
+ end
115
117
 
116
- def build_target
117
- deps = File.exist?(@deps) ?
118
- DepAnalyzer.read_from(@deps) :
119
- DepAnalyzer.new("./src").build_to_file(["./src", "./src/components"], @deps)
120
- target = "./target/#{@name}"
121
- build_time = File.exist?(target) ? File.mtime(target) : Time.new(0)
122
- files = DepAnalyzer.compiling_filter(deps, build_time, @source_suffix, @header_suffix)
118
+ def get_deps(dep_file, source_dir, include_dirs)
119
+ File.exist?(dep_file) ? DepAnalyzer.read_from(dep_file) :
120
+ DepAnalyzer.new(source_dir, @source_suffix, @header_suffix).build_to_file(include_dirs, dep_file)
121
+ end
122
+
123
+ def target_deps
124
+ get_deps @deps, @src_short, [@src_short, @components_short]
125
+ end
123
126
 
124
- if files.empty? && File.exist?(target)
125
- puts "nothing to do, all up to date"
126
- return
127
+ # contain only headers
128
+ # sources in ./src/components are not included
129
+ def tests_deps
130
+ get_deps @test_deps, @tests_short, [@src_short, @components_short]
127
131
  end
128
132
 
129
- self.send "build_#{@mode.to_s}", files
133
+ def build_target
134
+ puts "#{'[BUILDING TARGET]'.magenta}..."
135
+ deps = get_deps @deps, @src, [@src_short, @components_short]
136
+ target = "#{@target}/#{@name}"
137
+ build_time = File.exist?(target) ? File.mtime(target) : Time.new(0)
138
+ files = DepAnalyzer.compiling_filter deps, build_time, @source_suffix, @header_suffix
139
+
140
+ build_compiler_from_config
141
+
142
+ if files.empty? && File.exist?(target)
143
+ puts "nothing to do, all up to date"
144
+ return
145
+ end
146
+
147
+ self.send "build_#{@mode.to_s}", files
148
+ end
130
149
  end
131
150
  end