mtbuild 0.0.9 → 0.1.0

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,67 @@
1
+ module MTBuild
2
+
3
+ module Cleaner
4
+
5
+ @global_clean_list = ::Rake::FileList.new
6
+
7
+ class << self
8
+
9
+ # The list of files/folder to clean
10
+ attr_reader :global_clean_list
11
+
12
+ def generate_global_clean_task
13
+ desc "Remove intermediate and output files/folders for all projects."
14
+ task :clean do
15
+ cleanup_files(@global_clean_list)
16
+ end
17
+ end
18
+
19
+ def generate_clean_task_for_project(project_name, project_clean_list)
20
+ desc "Remove intermediate and output files/folders for #{project_name}."
21
+ task :clean do
22
+ cleanup_files(project_clean_list)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def cleanup_files(file_names)
29
+ file_names.each do |file_name|
30
+ cleanup(file_name)
31
+ end
32
+ end
33
+
34
+ def cleanup(file_name, opts={})
35
+ begin
36
+ Rake::FileUtilsExt.rm_r file_name, opts
37
+ rescue StandardError => ex
38
+ puts "Failed to remove #{file_name}: #{ex}" unless file_already_gone?(file_name)
39
+ end
40
+ end
41
+
42
+ def file_already_gone?(file_name)
43
+ return false if File.exist?(file_name)
44
+
45
+ path = file_name
46
+ prev = nil
47
+
48
+ while (path = File.dirname(path))
49
+ return false if cant_be_deleted?(path)
50
+ break if [prev, "."].include?(path)
51
+ prev = path
52
+ end
53
+ true
54
+ end
55
+
56
+ def cant_be_deleted?(path_name)
57
+ File.exist?(path_name) &&
58
+ (!File.readable?(path_name) || !File.executable?(path_name))
59
+ end
60
+
61
+ include Rake::DSL
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -15,25 +15,24 @@ module MTBuild
15
15
  # A list of Rake test tasks that will execute after this configuration builds
16
16
  attr_reader :tests
17
17
 
18
- def initialize(project_name, project_folder, output_folder, configuration_name, configuration)
18
+ def initialize(parent_project, output_folder, configuration_name, configuration)
19
19
  super
20
- check_configuration(configuration)
21
-
22
- @dependencies = configuration.fetch(:dependencies, [])
20
+ @dependencies = namespace_tasks(configuration.fetch(:dependencies, []))
21
+ @dependencies |= configuration.fetch(:rake_dependencies, [])
23
22
  @default_toolchain_config = configuration[:toolchain]
24
- @default_toolchain = Toolchain.create_toolchain(@default_toolchain_config)
23
+ @default_toolchain = Toolchain.create_toolchain(self, @default_toolchain_config)
25
24
 
26
- source_files = Utils.expand_file_list(configuration.fetch(:sources, []), configuration.fetch(:excludes, []), @project_folder)
27
- @toolchains = {@default_toolchain => source_files}
25
+ @source_files = Utils.expand_file_list(configuration.fetch(:sources, []), configuration.fetch(:excludes, []), @project_folder)
26
+ @toolchains = {@default_toolchain => @source_files}
28
27
 
29
- @tests = Utils.ensure_array(configuration.fetch(:tests, []))
28
+ @tests = namespace_tasks(Utils.ensure_array(configuration.fetch(:tests, [])))
30
29
  end
31
30
 
32
31
  # This method adds source files with their own toolchains. Use this method
33
32
  # to add any source files that need special toolchain settings.
34
33
  def add_sources(sources, excludes=[], toolchain_configuration)
35
34
  merged_configuration = Utils.merge_configurations(@default_toolchain_config, toolchain_configuration)
36
- toolchain = Toolchain.create_toolchain(merged_configuration)
35
+ toolchain = Toolchain.create_toolchain(self, merged_configuration)
37
36
  @toolchains[toolchain] = Utils.expand_file_list(sources, excludes, @project_folder)
38
37
  end
39
38
 
@@ -51,7 +50,8 @@ module MTBuild
51
50
  private
52
51
 
53
52
  def check_configuration(configuration)
54
- fail "No toolchain specified for #{@project_name}:#{@configuration_name}" if configuration.fetch(:toolchain, nil).nil?
53
+ super
54
+ fail "No toolchain specified for #{@parent_project.project_name}:#{@configuration_name}" if configuration.fetch(:toolchain, nil).nil?
55
55
  end
56
56
 
57
57
  def self.add_framework_dependencies_to_toolchain(toolchain, *dependencies)
@@ -8,8 +8,8 @@ module MTBuild
8
8
  # The configuration's name
9
9
  attr_reader :configuration_name
10
10
 
11
- # The name of the project that owns this configuration
12
- attr_reader :project_name
11
+ # The project that owns this configuration
12
+ attr_reader :parent_project
13
13
 
14
14
  # The project's folder. Relative path references are interpreted as
15
15
  # relative to this folder.
@@ -18,16 +18,16 @@ module MTBuild
18
18
  # The project's output folder. Project output goes here.
19
19
  attr_reader :output_folder
20
20
 
21
- def initialize(project_name, project_folder, output_folder, configuration_name, configuration)
22
- check_configuration(configuration)
23
- @project_name = project_name
24
- @project_folder = File.expand_path(project_folder)
21
+ def initialize(parent_project, output_folder, configuration_name, configuration)
22
+ @parent_project = parent_project
23
+ @project_folder = File.expand_path(@parent_project.project_folder)
25
24
  @configuration_name = configuration_name
26
- @output_folder = File.expand_path(File.join(output_folder, @project_name.to_s, @configuration_name.to_s))
25
+ @output_folder = File.expand_path(File.join(output_folder, @configuration_name.to_s))
26
+ check_configuration(configuration)
27
27
 
28
28
  @versioner = nil
29
29
  @versioner_config = configuration.fetch(:versioner, nil)
30
- @versioner = Versioner.create_versioner(@project_name, @project_folder, @output_folder, @configuration_name, @versioner_config) unless @versioner_config.nil?
30
+ @versioner = Versioner.create_versioner(@parent_project.project_name, @project_folder, @output_folder, @configuration_name, @versioner_config) unless @versioner_config.nil?
31
31
  end
32
32
 
33
33
  # Create the actual Rake tasks that will perform the configuration's work
@@ -40,6 +40,11 @@ module MTBuild
40
40
  def check_configuration(configuration)
41
41
  end
42
42
 
43
+ def namespace_tasks(task_list)
44
+ return task_list.collect {|task| "#{@parent_project.parent_workspace.workspace_name}:#{task}"} unless @parent_project.parent_workspace.nil?
45
+ return task_list
46
+ end
47
+
43
48
  include Rake::DSL
44
49
  end
45
50
 
@@ -6,8 +6,8 @@ module MTBuild
6
6
  # method will create this for you.
7
7
  class FrameworkConfiguration < Configuration
8
8
 
9
- def initialize(project_name, project_folder, output_folder, configuration_name, configuration, api_headers)
10
- super project_name, project_folder, output_folder, configuration_name, configuration
9
+ def initialize(parent_project, output_folder, configuration_name, configuration, api_headers)
10
+ super parent_project, output_folder, configuration_name, configuration
11
11
  check_configuration(configuration)
12
12
  @api_headers = api_headers
13
13
  @configuration_headers = Utils.expand_folder_list(configuration.fetch(:configuration_headers, []), @project_folder)
@@ -17,7 +17,7 @@ module MTBuild
17
17
  # Create the actual Rake tasks that will perform the configuration's work
18
18
  def configure_tasks
19
19
  super
20
- desc "Framework '#{@project_name}' with configuration '#{@configuration_name}'"
20
+ desc "Framework '#{@parent_project.project_name}' with configuration '#{@configuration_name}'"
21
21
  new_task = framework_task @configuration_name => @object_files do |t|
22
22
  puts "found framework #{t.name}."
23
23
  end
@@ -30,7 +30,7 @@ module MTBuild
30
30
 
31
31
  def check_configuration(configuration)
32
32
  super
33
- fail "No objects specified for #{@project_name}:#{@configuration_name}" if configuration.fetch(:objects, nil).nil?
33
+ fail "No objects specified for #{@parent_project.project_name}:#{@configuration_name}" if configuration.fetch(:objects, nil).nil?
34
34
  end
35
35
 
36
36
  end
@@ -16,9 +16,10 @@ module MTBuild
16
16
  # Adds a named FrameworkConfiguration to the project.
17
17
  def add_configuration(configuration_name, configuration)
18
18
  super
19
- default_configuration = Workspace.configuration_defaults.fetch(configuration_name, {})
19
+ default_configuration = {}
20
+ default_configuration = @parent_workspace.configuration_defaults.fetch(configuration_name, {}) unless @parent_workspace.nil?
20
21
  merged_configuration = Utils.merge_configurations(default_configuration, configuration)
21
- cfg = FrameworkConfiguration.new(@project_name, @project_folder, effective_output_folder, configuration_name, merged_configuration, @api_headers)
22
+ cfg = FrameworkConfiguration.new(self, effective_output_folder, configuration_name, merged_configuration, @api_headers)
22
23
  @configurations << cfg
23
24
  return cfg
24
25
  end
@@ -2,7 +2,6 @@ module MTBuild
2
2
 
3
3
  # Makefile loader to be used with the import file loader.
4
4
  class MakefileLoader
5
- include Rake::DSL
6
5
 
7
6
  SPACE_MARK = "\0"
8
7
 
@@ -35,6 +34,9 @@ module MTBuild
35
34
  def respace(str)
36
35
  str.tr SPACE_MARK, ' '
37
36
  end
37
+
38
+ include Rake::DSL
39
+
38
40
  end
39
41
 
40
42
  # Install the handler
@@ -82,13 +82,13 @@ module MTBuild
82
82
  end
83
83
 
84
84
  def define
85
- desc "Build the package"
85
+ desc "Build the package for #{@name}"
86
86
  task :package
87
87
 
88
- desc "Force a rebuild of the package files"
88
+ desc "Force a rebuild of the package files for #{@name}"
89
89
  task :repackage => [:clobber_package, :package]
90
90
 
91
- desc "Remove package products"
91
+ desc "Remove package products for #{@name}"
92
92
  task :clobber_package do
93
93
  rm_r package_dir rescue nil
94
94
  end
@@ -2,6 +2,8 @@ module MTBuild
2
2
 
3
3
  # This is the base class for all project types.
4
4
  class Project
5
+ require 'mtbuild/build_registry'
6
+ require 'mtbuild/cleaner'
5
7
 
6
8
  # The project's name
7
9
  attr_reader :project_name
@@ -13,29 +15,44 @@ module MTBuild
13
15
  # The project's output folder. Project output goes here.
14
16
  attr_reader :output_folder
15
17
 
18
+ # The project's parent workspace
19
+ attr_reader :parent_workspace
20
+
21
+ # The project's list of things to clean
22
+ attr_reader :clean_list
23
+
16
24
  # If supplied, the configuration_block will be passed the
17
25
  # newly-constructed Project object.
18
26
  def initialize(project_name, project_folder, &configuration_block)
19
27
  @configurations = []
20
- @default_tasks = []
21
- @project_name = project_name
22
28
  @project_folder = File.expand_path(project_folder)
23
29
  @output_folder = File.expand_path(File.join(@project_folder, MTBuild.default_output_folder))
30
+ @project_name, @parent_workspace = MTBuild::BuildRegistry.enter_project(project_name, self)
31
+ @clean_list = Rake::FileList.new
32
+
24
33
  configuration_block.call(self) if configuration_block
25
34
 
26
35
  namespace @project_name do
27
36
  @configurations.each do |configuration|
28
- configuration.configure_tasks()
37
+ configuration.configure_tasks
29
38
  end
39
+
40
+ Cleaner.generate_clean_task_for_project(@project_name, @clean_list)
30
41
  end
31
42
 
32
- # If there is no active workspace, set up any registered default project tasks
33
- task :default => @default_tasks unless Workspace.have_workspace?
43
+ #TODO: This is a strange way to do this. Should probably be moved to "application" functionality.
44
+ if @parent_workspace.nil? and Rake.application.lookup(:default).nil?
45
+ task :default do
46
+ puts 'Nothing to do. Use the -T flag to see the list of tasks you can build.'
47
+ end
48
+ end
49
+
50
+ MTBuild::BuildRegistry.exit_project
34
51
  end
35
52
 
36
- # Add tasks to be built by default if MTBuild is invoked with no arguments
37
- def add_default_tasks(default_tasks)
38
- @default_tasks |= Utils.ensure_array(default_tasks).flatten
53
+ # Get the fully-qualified task name for a configuration
54
+ def task_for_configuration(config_name)
55
+ "#{@project_name}:#{config_name}"
39
56
  end
40
57
 
41
58
  # Set the project's output folder.
@@ -43,16 +60,20 @@ module MTBuild
43
60
  @output_folder = File.expand_path(File.join(@project_folder, output_folder))
44
61
  end
45
62
 
63
+ # Add files to the project's clean list.
64
+ def add_files_to_clean(*filenames)
65
+ @clean_list.include(filenames)
66
+ Cleaner.global_clean_list.include(filenames)
67
+ end
68
+
46
69
  # Returns the effective output folder. If a workspace exists, this will
47
70
  # return the workspace's output folder. If not, it will return the
48
71
  # project's output folder.
49
72
  def effective_output_folder
50
- if Workspace.have_workspace?
51
- relative_project_location = Utils::path_difference(Workspace.workspace.workspace_folder, @project_folder)
52
- fail "Project folder '#{@project_folder}' must be within workspace folder '#{Workspace.workspace.workspace_folder}'." if relative_project_location.nil?
53
- return File.join(Workspace.workspace.output_folder, relative_project_location)
73
+ if MTBuild::BuildRegistry.top_workspace.nil?
74
+ File.join(@output_folder, @project_name.to_s.split(':'))
54
75
  else
55
- return @output_folder
76
+ File.join(MTBuild::BuildRegistry.top_workspace.output_folder, @project_name.to_s.split(':'))
56
77
  end
57
78
  end
58
79
 
@@ -6,9 +6,9 @@ module MTBuild
6
6
  # method will create this for you.
7
7
  class StaticLibraryConfiguration < CompiledConfiguration
8
8
 
9
- def initialize(project_name, project_folder, output_folder, configuration_name, configuration, api_headers)
9
+ def initialize(parent_project, output_folder, configuration_name, configuration, api_headers)
10
10
  @api_headers = api_headers
11
- super project_name, project_folder, output_folder, configuration_name, configuration
11
+ super parent_project, output_folder, configuration_name, configuration
12
12
  @configuration_headers = Utils.expand_folder_list(configuration.fetch(:configuration_headers, []), @project_folder)
13
13
  end
14
14
 
@@ -24,10 +24,10 @@ module MTBuild
24
24
  all_object_folders |= object_folders
25
25
  end
26
26
 
27
- library_files, library_folders = @default_toolchain.create_static_library_tasks(all_object_files, @project_name)
27
+ library_files, library_folders = @default_toolchain.create_static_library_tasks(all_object_files, @parent_project.project_name)
28
28
  dependencies = @dependencies+all_object_folders+library_folders+library_files
29
29
 
30
- desc "Build library '#{@project_name}' with configuration '#{@configuration_name}'"
30
+ desc "Build library '#{@parent_project.project_name}' with configuration '#{@configuration_name}'"
31
31
  new_task = static_library_task @configuration_name => dependencies do |t|
32
32
  puts "built library #{t.name}."
33
33
  @tests.each do |test|
@@ -19,9 +19,10 @@ module MTBuild
19
19
  # Adds a named static library configuration to the project.
20
20
  def add_configuration(configuration_name, configuration)
21
21
  super
22
- default_configuration = Workspace.configuration_defaults.fetch(configuration_name, {})
22
+ default_configuration = {}
23
+ default_configuration = @parent_workspace.configuration_defaults.fetch(configuration_name, {}) unless @parent_workspace.nil?
23
24
  merged_configuration = Utils.merge_configurations(default_configuration, configuration)
24
- cfg = StaticLibraryConfiguration.new(@project_name, @project_folder, effective_output_folder, configuration_name, merged_configuration, @api_headers)
25
+ cfg = StaticLibraryConfiguration.new(self, effective_output_folder, configuration_name, merged_configuration, @api_headers)
25
26
  @configurations << cfg
26
27
  return cfg
27
28
  end
@@ -54,7 +55,7 @@ module MTBuild
54
55
  end
55
56
  end
56
57
 
57
- framework_rakefile = File.join(framework_task.package_dir_path, "Rakefile.rb")
58
+ framework_rakefile = File.join(framework_task.package_dir_path, "mtbuildfile.rb")
58
59
  file framework_rakefile do |f|
59
60
  fdir = File.dirname(framework_rakefile)
60
61
  mkdir_p(fdir) unless File.exist?(fdir)
@@ -17,10 +17,10 @@ module MTBuild
17
17
  all_object_folders |= object_folders
18
18
  end
19
19
 
20
- application_binaries, application_files, application_folders = @default_toolchain.create_application_tasks(all_object_files, @project_name)
20
+ application_binaries, application_files, application_folders = @default_toolchain.create_application_tasks(all_object_files, @parent_project.project_name)
21
21
  dependencies = @dependencies+all_object_folders+application_folders+application_files+application_binaries
22
22
 
23
- desc "Build and run test application '#{@project_name}' with configuration '#{@configuration_name}'"
23
+ desc "Build and run test application '#{@parent_project.project_name}' with configuration '#{@configuration_name}'"
24
24
  new_task = test_application_task @configuration_name => dependencies do |t|
25
25
  puts "built test application #{t.name}."
26
26
  sh "\"#{application_binaries.first}\""
@@ -10,9 +10,10 @@ module MTBuild
10
10
  # Adds a named test application configuration to the project.
11
11
  def add_configuration(configuration_name, configuration)
12
12
  super
13
- default_configuration = Workspace.configuration_defaults.fetch(configuration_name, {})
13
+ default_configuration = {}
14
+ default_configuration = @parent_workspace.configuration_defaults.fetch(configuration_name, {}) unless @parent_workspace.nil?
14
15
  merged_configuration = Utils.merge_configurations(default_configuration, configuration)
15
- cfg = TestApplicationConfiguration.new(@project_name, @project_folder, effective_output_folder, configuration_name, merged_configuration)
16
+ cfg = TestApplicationConfiguration.new(self, effective_output_folder, configuration_name, merged_configuration)
16
17
  @configurations << cfg
17
18
  return cfg
18
19
  end
@@ -13,17 +13,21 @@ module MTBuild
13
13
  # Text to append to the name of output files
14
14
  attr_accessor :output_decorator
15
15
 
16
- def initialize(configuration)
16
+ # parent configuration
17
+ attr_accessor :parent_configuration
18
+
19
+ def initialize(parent_configuration, toolchain_configuration)
17
20
  @output_folder = ''
18
21
  @project_folder = ''
19
22
  @output_decorator = ''
20
23
  @include_objects = []
21
24
  @include_paths = []
22
25
  @library_paths = []
26
+ @parent_configuration = parent_configuration
23
27
 
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, [])))
28
+ add_include_paths(expand_project_relative_paths(toolchain_configuration.fetch(:include_paths, [])))
29
+ add_include_objects(expand_project_relative_paths(toolchain_configuration.fetch(:include_objects, [])))
30
+ add_library_paths(expand_project_relative_paths(toolchain_configuration.fetch(:library_paths, [])))
27
31
  end
28
32
 
29
33
  # Retrieve a list of additional objects to link with
@@ -86,7 +90,7 @@ module MTBuild
86
90
  @registered_toolchains[toolchain_name] = toolchain_class;
87
91
  end
88
92
 
89
- def self.create_toolchain(toolchain_configuration)
93
+ def self.create_toolchain(parent_configuration, toolchain_configuration)
90
94
  toolchain_name = toolchain_configuration.fetch(:name, nil)
91
95
  fail "error: toolchain name not specified." if toolchain_name.nil?
92
96
 
@@ -100,7 +104,7 @@ module MTBuild
100
104
  end
101
105
  toolchain_class = @registered_toolchains.fetch(toolchain_name, nil)
102
106
  fail "error: toolchain #{toolchain_name} could not be found." if toolchain_class.nil?
103
- return Object::const_get(toolchain_class).new(toolchain_configuration)
107
+ return Object::const_get(toolchain_class).new(parent_configuration, toolchain_configuration)
104
108
  end
105
109
 
106
110
  include Rake::DSL
@@ -6,7 +6,7 @@ module MTBuild
6
6
  # This ToolchainGcc subclass can build using arm-non-eabi-gcc
7
7
  class ToolchainArmNoneEabiGcc < ToolchainGcc
8
8
 
9
- def initialize(configuration)
9
+ def initialize(parent_configuration, toolchain_configuration)
10
10
  super
11
11
  end
12
12
 
@@ -17,9 +17,14 @@ module MTBuild
17
17
  hex_file = File.join(@output_folder, "#{executable_name}#{@output_decorator}.hex")
18
18
  map_file = File.join(@output_folder, "#{executable_name}#{@output_decorator}.map")
19
19
  executable_folder = @output_folder
20
- directory executable_folder
21
- CLOBBER.include(executable_folder)
22
- CLOBBER.include(elf_file)
20
+
21
+ unless @tracked_folders.include?executable_folder
22
+ @tracked_folders << executable_folder
23
+ directory executable_folder
24
+ @parent_configuration.parent_project.add_files_to_clean(executable_folder)
25
+ end
26
+
27
+ @parent_configuration.parent_project.add_files_to_clean(elf_file, bin_file, hex_file, map_file)
23
28
 
24
29
  all_objects = objects+get_include_objects
25
30