mtbuild 0.0.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.
@@ -0,0 +1,21 @@
1
+ module MTBuild
2
+ require "mtbuild/staticlibrary_configuration"
3
+ require 'mtbuild/project'
4
+
5
+ # This class is used to build static libraries. A static library has
6
+ # compilation and archival phases that produce a binary library package.
7
+ class StaticLibraryProject < Project
8
+
9
+ # Adds a named static library configuration to the project.
10
+ def add_configuration(configuration_name, configuration)
11
+ super
12
+ default_configuration = Workspace.configuration_defaults.fetch(configuration_name, {})
13
+ merged_configuration = Utils.merge_configurations(default_configuration, configuration)
14
+ cfg = StaticLibraryConfiguration.new(@project_name, @project_folder, effective_output_folder, configuration_name, merged_configuration)
15
+ @configurations << cfg
16
+ return cfg
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,31 @@
1
+ module Rake
2
+
3
+ require 'rake'
4
+
5
+ # This is a top-level Rake task for creating a static library
6
+ class StaticLibraryTask < Task
7
+
8
+ # API header location for the static library
9
+ attr_accessor :api_headers
10
+
11
+ # The static library output file(s)
12
+ attr_accessor :library_files
13
+
14
+ def initialize(task_name, app)
15
+ super
16
+ @api_headers = ''
17
+ @library_files = ''
18
+ end
19
+
20
+ end
21
+
22
+ module DSL
23
+
24
+ # DSL method to create a static library task.
25
+ def static_library_task(*args, &block)
26
+ new_task = Rake::StaticLibraryTask.define_task(*args, &block)
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,33 @@
1
+ module MTBuild
2
+ require 'mtbuild/compiled_configuration'
3
+
4
+ # Use this class to create test application configurations. You won't typically
5
+ # instantiate this directly. Instead, the TestApplicationProject.add_configuration
6
+ # method will create this for you.
7
+ class TestApplicationConfiguration < CompiledConfiguration
8
+
9
+ # Create the actual Rake tasks that will perform the configuration's work
10
+ def configure_tasks
11
+ super
12
+ all_object_files = []
13
+ all_object_folders = []
14
+ @toolchains.each do |toolchain, sources|
15
+ object_files, object_folders = toolchain.create_compile_tasks(sources)
16
+ all_object_files |= object_files
17
+ all_object_folders |= object_folders
18
+ end
19
+
20
+ application_binaries, application_files, application_folders = @default_toolchain.create_application_tasks(all_object_files, @project_name)
21
+ dependencies = @dependencies+all_object_folders+application_folders+application_files+application_binaries
22
+
23
+ desc "Build and run test application '#{@project_name}' with configuration '#{@configuration_name}'"
24
+ new_task = test_application_task @configuration_name => dependencies do |t|
25
+ puts "built test application #{t.name}."
26
+ sh application_binaries.first
27
+ puts "ran test application #{t.name}."
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,22 @@
1
+ module MTBuild
2
+ require "mtbuild/test_application_configuration"
3
+ require 'mtbuild/project'
4
+
5
+ # This class is used to build test applications. A test application has
6
+ # compilation and link phases that produce a binary test executable. The test
7
+ # executable is invoked after building successfully.
8
+ class TestApplicationProject < Project
9
+
10
+ # Adds a named test application configuration to the project.
11
+ def add_configuration(configuration_name, configuration)
12
+ super
13
+ default_configuration = Workspace.configuration_defaults.fetch(configuration_name, {})
14
+ merged_configuration = Utils.merge_configurations(default_configuration, configuration)
15
+ cfg = TestApplicationConfiguration.new(@project_name, @project_folder, effective_output_folder, configuration_name, merged_configuration)
16
+ @configurations << cfg
17
+ return cfg
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,23 @@
1
+ module Rake
2
+
3
+ require 'rake'
4
+
5
+ # This is a top-level Rake task for creating a test application
6
+ class TestApplicationTask < Task
7
+
8
+ def initialize(task_name, app)
9
+ super
10
+ end
11
+
12
+ end
13
+
14
+ module DSL
15
+
16
+ # DSL method to create a test application task.
17
+ def test_application_task(*args, &block)
18
+ new_task = Rake::TestApplicationTask.define_task(*args, &block)
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,109 @@
1
+ module MTBuild
2
+
3
+ # This is the base class for all toolchain types.
4
+ class Toolchain
5
+
6
+ # The toolchain's output folder
7
+ attr_accessor :output_folder
8
+
9
+ # The project's folder. Relative path references are interpreted as
10
+ # relative to this folder.
11
+ attr_accessor :project_folder
12
+
13
+ # Text to append to the name of output files
14
+ attr_accessor :output_decorator
15
+
16
+ def initialize(configuration)
17
+ @output_folder = ''
18
+ @project_folder = ''
19
+ @output_decorator = ''
20
+ @include_objects = []
21
+ @include_paths = []
22
+ @library_paths = []
23
+
24
+ add_include_paths(expand_project_relative_paths(configuration.fetch(:include_paths, [])))
25
+ add_include_objects(expand_project_relative_paths(configuration.fetch(:include_objects, [])))
26
+ add_library_paths(expand_project_relative_paths(configuration.fetch(:library_paths, [])))
27
+ end
28
+
29
+ # Retrieve a list of additional objects to link with
30
+ def get_include_objects
31
+ @include_objects.collect {|i| File.expand_path(i.gsub('$(PROJECT_DIR)', @project_folder))}
32
+ end
33
+
34
+ # Retrieve a list of include paths to compile with
35
+ def get_include_paths
36
+ @include_paths.collect {|i| File.expand_path(i.gsub('$(PROJECT_DIR)', @project_folder))}
37
+ end
38
+
39
+ # Retrieve a list of library paths to link with
40
+ def get_library_paths
41
+ @library_paths.collect {|i| File.expand_path(i.gsub('$(PROJECT_DIR)', @project_folder))}
42
+ end
43
+
44
+ # Add an additional object to the list of additional objects to link with
45
+ def add_include_objects(include_objects)
46
+ include_objects = Utils.ensure_array(include_objects).to_a.flatten
47
+ @include_objects |= include_objects
48
+ end
49
+
50
+ # Add an include path to the list of include paths to compile with
51
+ def add_include_paths(include_paths)
52
+ include_paths = Utils.ensure_array(include_paths).to_a.flatten
53
+ @include_paths |= include_paths
54
+ end
55
+
56
+ # Add a library path to the list of library paths to link with
57
+ def add_library_paths(library_paths)
58
+ library_paths = Utils.ensure_array(library_paths).to_a.flatten
59
+ @library_paths |= library_paths
60
+ end
61
+
62
+ # Create Rake tasks for compilation
63
+ def create_compile_tasks(source_files)
64
+ fail "Toolchain didn't provide create_compile_tasks"
65
+ end
66
+
67
+ # Create Rake tasks for archival
68
+ def create_static_library_tasks(objects, library_name)
69
+ fail "Toolchain didn't provide create_static_library_tasks"
70
+ end
71
+
72
+ # Create Rake tasks for linking
73
+ def create_application_tasks(objects, executable_name)
74
+ fail "Toolchain didn't provide create_executable_tasks"
75
+ end
76
+
77
+ private
78
+
79
+ def expand_project_relative_paths(paths)
80
+ return Utils.ensure_array(paths).to_a.flatten.collect{ |p| (File.join('$(PROJECT_DIR)', p))}
81
+ end
82
+
83
+ @registered_toolchains = {}
84
+
85
+ def self.register_toolchain(toolchain_name, toolchain_class)
86
+ @registered_toolchains[toolchain_name] = toolchain_class;
87
+ end
88
+
89
+ def self.create_toolchain(toolchain_configuration)
90
+ toolchain_name = toolchain_configuration.fetch(:name, nil)
91
+ fail "error: toolchain name not specified." if toolchain_name.nil?
92
+
93
+ toolchain_class = @registered_toolchains.fetch(toolchain_name, nil)
94
+ if !toolchain_class
95
+ toolchain_file = File.join('mtbuild', 'toolchains', toolchain_name.to_s)
96
+ begin
97
+ require toolchain_file
98
+ rescue LoadError
99
+ end
100
+ end
101
+ toolchain_class = @registered_toolchains.fetch(toolchain_name, nil)
102
+ fail "error: toolchain #{toolchain_name} could not be found." if toolchain_class.nil?
103
+ return Object::const_get(toolchain_class).new(toolchain_configuration)
104
+ end
105
+
106
+ include Rake::DSL
107
+ end
108
+
109
+ end
@@ -0,0 +1,69 @@
1
+ module MTBuild
2
+ require 'mtbuild/toolchains/gcc'
3
+
4
+ Toolchain.register_toolchain(:arm_none_eabi_gcc, 'MTBuild::ToolchainArmNoneEabiGcc')
5
+
6
+ # This ToolchainGcc subclass can build using arm-non-eabi-gcc
7
+ class ToolchainArmNoneEabiGcc < ToolchainGcc
8
+
9
+ def initialize(configuration)
10
+ super
11
+ end
12
+
13
+ # Create Rake tasks for linking
14
+ def create_application_tasks(objects, executable_name)
15
+ elf_file = File.join(@output_folder, "#{executable_name}#{@output_decorator}.elf")
16
+ bin_file = File.join(@output_folder, "#{executable_name}#{@output_decorator}.bin")
17
+ hex_file = File.join(@output_folder, "#{executable_name}#{@output_decorator}.hex")
18
+ map_file = File.join(@output_folder, "#{executable_name}#{@output_decorator}.map")
19
+ executable_folder = @output_folder
20
+ directory executable_folder
21
+ CLOBBER.include(executable_folder)
22
+ CLOBBER.include(elf_file)
23
+
24
+ all_objects = objects+get_include_objects
25
+
26
+ file elf_file => all_objects do |t|
27
+ command_line = construct_link_command(all_objects, t.name, get_include_paths, get_library_paths, map_file)
28
+ sh command_line
29
+ end
30
+
31
+ file map_file => elf_file
32
+
33
+ file bin_file => elf_file do |t|
34
+ command_line = construct_objcopy_command(elf_file, t.name, ' -Obinary')
35
+ sh command_line
36
+ end
37
+ file hex_file => elf_file do |t|
38
+ command_line = construct_objcopy_command(elf_file, t.name, ' -Oihex')
39
+ sh command_line
40
+ end
41
+
42
+ return [elf_file, bin_file, hex_file], [map_file], [executable_folder]
43
+ end
44
+
45
+ private
46
+
47
+ def construct_objcopy_command(input_name, output_name, objcopyflags)
48
+ return "#{objcopy}#{objcopyflags} #{input_name} #{output_name}"
49
+ end
50
+
51
+ def compiler
52
+ return 'arm-none-eabi-gcc'
53
+ end
54
+
55
+ def archiver
56
+ return 'arm-none-eabi-ar'
57
+ end
58
+
59
+ def linker
60
+ return 'arm-none-eabi-gcc'
61
+ end
62
+
63
+ def objcopy
64
+ return 'arm-none-eabi-objcopy'
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,160 @@
1
+ module MTBuild
2
+ require 'mtbuild/toolchain'
3
+ require 'mtbuild/utils'
4
+ require 'rake/clean'
5
+ require 'rake/loaders/makefile'
6
+
7
+ Toolchain.register_toolchain(:gcc, 'MTBuild::ToolchainGcc')
8
+
9
+ # This Toolchain subclass can build using GCC
10
+ class ToolchainGcc < Toolchain
11
+
12
+ attr_accessor :cppflags, :cflags, :cxxflags, :asflags, :ldflags, :linker_script
13
+
14
+ def initialize(configuration)
15
+ super
16
+
17
+ begin
18
+ toolchain_test_output=%x{#{compiler} --version 2>&1}
19
+ toolchain_test_passed=$?.success?
20
+ rescue
21
+ toolchain_test_passed = false
22
+ end
23
+ fail "Toolchain component #{compiler} not found." unless toolchain_test_passed
24
+
25
+ @compiler_is_LLVM_gcc = toolchain_test_output.include?'LLVM'
26
+ @cppflags = configuration.fetch(:cppflags, '')
27
+ @cflags = configuration.fetch(:cflags, '')
28
+ @cxxflags = configuration.fetch(:cxxflags, '')
29
+ @asflags = configuration.fetch(:asflags, '')
30
+ @ldflags = configuration.fetch(:ldflags, '')
31
+ @linker_script = configuration.fetch(:linker_script, '')
32
+ end
33
+
34
+ # Create Rake tasks for compilation
35
+ def create_compile_tasks(source_files)
36
+ object_files = []
37
+ object_folders = []
38
+
39
+ source_files.each do |source_file|
40
+
41
+ relative_source_location = Utils::path_difference(@project_folder, File.dirname(source_file))
42
+ fail "Source file #{source_file} must be within #{@project_folder}." if relative_source_location.nil?
43
+ output_folder = File.join(@output_folder, relative_source_location)
44
+
45
+ object_folders << output_folder unless object_folders.include?output_folder
46
+
47
+ directory output_folder
48
+ CLOBBER.include(output_folder)
49
+
50
+ object_file = File.join(output_folder, source_file.pathmap('%n.o'))
51
+ dependency_file = File.join(output_folder, source_file.pathmap('%n.d'))
52
+
53
+ object_files << object_file
54
+ CLEAN.include(object_file)
55
+ CLEAN.include(dependency_file)
56
+
57
+ file_type = get_file_type(source_file)
58
+
59
+ file object_file => [source_file] do |t|
60
+ command_line = construct_compile_command(file_type, [source_file], get_include_paths, t.name)
61
+ sh command_line
62
+ end
63
+ Rake::MakefileLoader.new.load(dependency_file) if File.file?(dependency_file)
64
+ end
65
+ return object_files, object_folders
66
+ end
67
+
68
+ # Create Rake tasks for archival
69
+ def create_static_library_tasks(objects, library_name)
70
+ library_file = File.join(@output_folder, "lib#{library_name}#{@output_decorator}.a")
71
+ library_folder = @output_folder
72
+ directory library_folder
73
+ CLOBBER.include(library_folder)
74
+ CLOBBER.include(library_file)
75
+
76
+ file library_file => objects do |t|
77
+ command_line = construct_archive_command(objects, t.name)
78
+ sh command_line
79
+ end
80
+ return [library_file], [library_folder]
81
+ end
82
+
83
+ # Create Rake tasks for linking
84
+ def create_application_tasks(objects, executable_name)
85
+ elf_file = File.join(@output_folder, "#{executable_name}#{@output_decorator}")
86
+ map_file = File.join(@output_folder, "#{executable_name}#{@output_decorator}.map")
87
+ executable_folder = @output_folder
88
+ directory executable_folder
89
+ CLOBBER.include(executable_folder)
90
+ CLOBBER.include(elf_file)
91
+
92
+ all_objects = objects+get_include_objects
93
+
94
+ file elf_file => all_objects do |t|
95
+ command_line = construct_link_command(all_objects, t.name, get_include_paths, get_library_paths, map_file)
96
+ sh command_line
97
+ end
98
+
99
+ file map_file => elf_file
100
+
101
+ return [elf_file], [map_file], [executable_folder]
102
+ end
103
+
104
+ private
105
+
106
+ def get_file_type(source_file)
107
+ file_extension = File.extname(source_file)
108
+ return :cplusplus if ['.cc', '.cp', '.cxx', '.cpp', '.CPP', '.c++', '.C'].include? file_extension
109
+ return :asm if ['.s', '.S', '.sx'].include? file_extension
110
+ return :c
111
+ end
112
+
113
+ def construct_compile_command(file_type, prerequisites, include_paths, output_name)
114
+ prerequisites_s = prerequisites.empty? ? '' : " #{prerequisites.join(' ')}"
115
+ include_paths_s = include_paths.empty? ? '' : " -I#{include_paths.join(' -I')}"
116
+ cppflags_s = @cppflags.empty? ? '' : " #{@cppflags}"
117
+ cflags_s = @cflags.empty? ? '' : " #{@cflags}"
118
+ cxxflags_s = @cxxflags.empty? ? '' : " #{@cxxflags}"
119
+ asflags_s = @asflags.empty? ? '' : " #{@asflags}"
120
+ return "#{compiler}#{cppflags_s}#{cflags_s}#{prerequisites_s}#{include_paths_s} -MMD -c -o #{output_name}" if file_type == :c
121
+ return "#{compiler}#{cppflags_s}#{cxxflags_s}#{prerequisites_s}#{include_paths_s} -MMD -c -o #{output_name}" if file_type == :cplusplus
122
+ return "#{compiler}#{cppflags_s}#{asflags_s}#{prerequisites_s}#{include_paths_s} -MMD -c -o #{output_name}" if file_type == :asm
123
+ end
124
+
125
+ def construct_archive_command(prerequisites, output_name)
126
+ prerequisites_s = prerequisites.empty? ? '' : " #{prerequisites.join(' ')}"
127
+ return "#{archiver} rcs #{output_name} #{prerequisites_s}"
128
+ end
129
+
130
+ def construct_link_command(prerequisites, output_name, include_paths, library_paths, map_name)
131
+ prerequisites_s = prerequisites.empty? ? '' : " #{prerequisites.join(' ')}"
132
+ include_paths_s = include_paths.empty? ? '' : " -I#{include_paths.join(' -I')}"
133
+ library_paths_s = library_paths.empty? ? '' : " -L#{library_paths.join(' -L')}"
134
+ cppflags_s = @cppflags.empty? ? '' : " #{@cppflags}"
135
+ cflags_s = @cflags.empty? ? '' : " #{@cflags}"
136
+ ldflags_s = @ldflags.empty? ? '' : " #{@ldflags}"
137
+ linker_script_s = @linker_script.empty? ? '' : " -Wl,-T#{File.join(@project_folder,@linker_script)}"
138
+ return "#{compiler}#{cppflags_s}#{cflags_s}#{ldflags_s}#{linker_script_s}#{prerequisites_s}#{include_paths_s} #{map_flag(map_name)} -o #{output_name}"
139
+ end
140
+
141
+ def map_flag(map_file)
142
+ return "-Wl,-map,#{map_file}" if @compiler_is_LLVM_gcc
143
+ return "-Wl,-Map=#{map_file},--cref"
144
+ end
145
+
146
+ def compiler
147
+ return 'gcc'
148
+ end
149
+
150
+ def archiver
151
+ return 'ar'
152
+ end
153
+
154
+ def linker
155
+ return 'gcc'
156
+ end
157
+
158
+ end
159
+
160
+ end