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.
data/bin/mtbuild ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_path_expanded = File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
4
+ if File.directory?(lib_path_expanded)
5
+ $:.unshift(lib_path_expanded) unless $:.include?(lib_path_expanded)
6
+ end
7
+
8
+ require 'mtbuild'
9
+
10
+ Rake.application.run
data/lib/mtbuild.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'rake'
2
+
3
+ require "mtbuild/application"
4
+ require "mtbuild/application_project"
5
+ require "mtbuild/application_task"
6
+ require "mtbuild/dsl"
7
+ require "mtbuild/mtbuild"
8
+ require "mtbuild/staticlibrary_project"
9
+ require "mtbuild/staticlibrary_task"
10
+ require "mtbuild/test_application_project"
11
+ require "mtbuild/test_application_task"
12
+ require "mtbuild/version"
13
+ require "mtbuild/workspace"
@@ -0,0 +1,39 @@
1
+ module Rake
2
+ class << self
3
+ # Singleton MTBuild application instance
4
+ def application
5
+ @application ||= MTBuild::Application.new
6
+ end
7
+ end
8
+ end
9
+
10
+ module MTBuild
11
+
12
+ require 'rake'
13
+
14
+ # This subclasses the Rake::Application class only only to inject the MTBuild
15
+ # version number for display when mtbuild is invoked with the --version flag.
16
+ class Application < Rake::Application
17
+
18
+ # This hijacks the "--version" flag and displays the MTBuild version along
19
+ # with the Rake version. All other options/flags are returned unmodified.
20
+ def standard_rake_options
21
+ return super.map do |opt|
22
+ if opt.first == '--version'
23
+ ['--version', '-V',
24
+ "Display the program version.",
25
+ lambda { |value|
26
+ puts "mtbuild, version #{MTBuild::VERSION}"
27
+ puts "rake, version #{RAKEVERSION}"
28
+ exit
29
+ }
30
+ ]
31
+ else
32
+ opt
33
+ end
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,38 @@
1
+ module MTBuild
2
+ require 'mtbuild/compiled_configuration'
3
+
4
+ # Use this class to create application configurations. You won't typically
5
+ # instantiate this directly. Instead, the ApplicationProject.add_configuration
6
+ # method will create this for you.
7
+ class ApplicationConfiguration < 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 application '#{@project_name}' with configuration '#{@configuration_name}'"
24
+ new_task = application_task @configuration_name => dependencies do |t|
25
+ puts "built application #{t.name}."
26
+ @tests.each do |test|
27
+ if Rake::Task.task_defined? test
28
+ Rake::Task[test].invoke
29
+ else
30
+ $stderr.puts "warning: Skipping unknown test '#{test}'"
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,21 @@
1
+ module MTBuild
2
+ require "mtbuild/application_configuration"
3
+ require 'mtbuild/project'
4
+
5
+ # This class is used to build applications. An application has compilation
6
+ # and link phases that produce a binary executable.
7
+ class ApplicationProject < Project
8
+
9
+ # Adds a named ApplicationConfiguration 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 = ApplicationConfiguration.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,23 @@
1
+ module Rake
2
+
3
+ require 'rake'
4
+
5
+ # This is a top-level Rake task for creating an application
6
+ class ApplicationTask < 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 an application task.
17
+ def application_task(*args, &block)
18
+ new_task = Rake::ApplicationTask.define_task(*args, &block)
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,73 @@
1
+ module MTBuild
2
+ require 'mtbuild/configuration'
3
+ require 'mtbuild/toolchain'
4
+
5
+ # This is the base class for configurations representing compiled objects
6
+ # (libraries, applications, etc.)
7
+ class CompiledConfiguration < Configuration
8
+
9
+ # A list of Rake tasks that this configuration depends upon
10
+ attr_reader :dependencies
11
+
12
+ # A list of source files that will be compiled
13
+ attr_reader :source_files
14
+
15
+ # A list of Rake test tasks that will execute after this configuration builds
16
+ attr_reader :tests
17
+
18
+ def initialize(project_name, project_folder, output_folder, configuration_name, configuration)
19
+ super
20
+ check_configuration(configuration)
21
+
22
+ @dependencies = configuration.fetch(:dependencies, [])
23
+ @default_toolchain_config = configuration[:toolchain]
24
+ @default_toolchain = Toolchain.create_toolchain(@default_toolchain_config)
25
+
26
+ source_files = Utils.expand_file_list(configuration.fetch(:sources, []), configuration.fetch(:excludes, []), @project_folder)
27
+ @toolchains = {@default_toolchain => source_files}
28
+
29
+ @tests = Utils.ensure_array(configuration.fetch(:tests, []))
30
+ end
31
+
32
+ # This method adds source files with their own toolchains. Use this method
33
+ # to add any source files that need special toolchain settings.
34
+ def add_sources(sources, excludes=[], toolchain_configuration)
35
+ merged_configuration = Utils.merge_configurations(@default_toolchain_config, toolchain_configuration)
36
+ toolchain = Toolchain.create_toolchain(merged_configuration)
37
+ @toolchains[toolchain] = Utils.expand_file_list(sources, excludes, @project_folder)
38
+ end
39
+
40
+ # Create the actual Rake tasks that will perform the configuration's work
41
+ def configure_tasks
42
+ super
43
+ @toolchains.each do |toolchain, sources|
44
+ toolchain.output_folder = @output_folder
45
+ toolchain.project_folder = @project_folder
46
+ toolchain.output_decorator = "-#{@configuration_name}"
47
+ CompiledConfiguration.add_framework_dependencies_to_toolchain(toolchain, @dependencies)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def check_configuration(configuration)
54
+ fail "No toolchain specified for #{@project_name}:#{@configuration_name}" if configuration.fetch(:toolchain, nil).nil?
55
+ end
56
+
57
+ def self.add_framework_dependencies_to_toolchain(toolchain, *dependencies)
58
+ dependencies.each do |dependency|
59
+ if dependency.respond_to? :to_ary
60
+ CompiledConfiguration.add_framework_dependencies_to_toolchain(toolchain, *dependency.to_ary)
61
+ else
62
+ task_dependency = Rake.application.lookup(dependency)
63
+ fail "Unable to locate task for dependency: #{dependency}" if task_dependency.nil?
64
+ toolchain.add_include_paths(task_dependency.api_headers) if task_dependency.respond_to? :api_headers
65
+ toolchain.add_include_objects(task_dependency.library_files) if task_dependency.respond_to? :library_files
66
+ end
67
+ end
68
+ end
69
+
70
+ include Rake::DSL
71
+ end
72
+
73
+ end
@@ -0,0 +1,46 @@
1
+ module MTBuild
2
+
3
+ require 'mtbuild/versioner'
4
+
5
+ # This is the base class for all configuration types.
6
+ class Configuration
7
+
8
+ # The configuration's name
9
+ attr_reader :configuration_name
10
+
11
+ # The name of the project that owns this configuration
12
+ attr_reader :project_name
13
+
14
+ # The project's folder. Relative path references are interpreted as
15
+ # relative to this folder.
16
+ attr_reader :project_folder
17
+
18
+ # The project's output folder. Project output goes here.
19
+ attr_reader :output_folder
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)
25
+ @configuration_name = configuration_name
26
+ @output_folder = File.expand_path(File.join(output_folder, @project_name.to_s, @configuration_name.to_s))
27
+
28
+ @versioner = nil
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?
31
+ end
32
+
33
+ # Create the actual Rake tasks that will perform the configuration's work
34
+ def configure_tasks
35
+ @versioner.create_version_tasks unless @versioner.nil?
36
+ end
37
+
38
+ private
39
+
40
+ def check_configuration(configuration)
41
+ end
42
+
43
+ include Rake::DSL
44
+ end
45
+
46
+ end
@@ -0,0 +1,46 @@
1
+ module MTBuild
2
+
3
+ module DSL
4
+
5
+ # Defines a Workspace
6
+ def workspace(workspace_name, workspace_folder, &configuration_block)
7
+ MTBuild::Workspace.new(workspace_name, workspace_folder, &configuration_block)
8
+ end
9
+
10
+ # Defines an ApplicationProject
11
+ def application_project(application_name, project_folder, &configuration_block)
12
+ MTBuild::ApplicationProject.new(application_name, project_folder, &configuration_block)
13
+ end
14
+
15
+ # Defines a StaticLibraryProject
16
+ def static_library_project(library_name, project_folder, &configuration_block)
17
+ MTBuild::StaticLibraryProject.new(library_name, project_folder, &configuration_block)
18
+ end
19
+
20
+ # Defines a TestApplicationProject
21
+ def test_application_project(application_name, project_folder, &configuration_block)
22
+ MTBuild::TestApplicationProject.new(application_name, project_folder, &configuration_block)
23
+ end
24
+
25
+ # Defines a Toolchain
26
+ def toolchain(toolchain_name, toolchain_configuration={})
27
+ fail "error: the toolchain configuration is expected to be a hash." unless toolchain_configuration.is_a? Hash
28
+ toolchain_configuration[:name] = toolchain_name
29
+ return toolchain_configuration
30
+ end
31
+
32
+ # Defines a Versioner
33
+ def versioner(versioner_name, versioner_configuration={})
34
+ fail "error: the version file configuration is expected to be a hash." unless versioner_configuration.is_a? Hash
35
+ versioner_configuration[:name] = versioner_name
36
+ return versioner_configuration
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ # Extend the main object with the DSL commands. This allows top-level calls to
44
+ # workspace, application_project, etc. to work from a Rakefile without
45
+ # polluting the object inheritance tree.
46
+ self.extend MTBuild::DSL
@@ -0,0 +1,10 @@
1
+ module MTBuild
2
+
3
+ @default_output_folder = 'build'
4
+
5
+ # This returns the default name that MTBuild uses for the build output folder
6
+ def self.default_output_folder
7
+ return @default_output_folder
8
+ end
9
+
10
+ end
@@ -0,0 +1,71 @@
1
+ module MTBuild
2
+
3
+ # This is the base class for all project types.
4
+ class Project
5
+
6
+ # The project's name
7
+ attr_reader :project_name
8
+
9
+ # The project's folder. Relative path references are interpreted as
10
+ # relative to this folder.
11
+ attr_reader :project_folder
12
+
13
+ # The project's output folder. Project output goes here.
14
+ attr_reader :output_folder
15
+
16
+ # If supplied, the configuration_block will be passed the
17
+ # newly-constructed Project object.
18
+ def initialize(project_name, project_folder, &configuration_block)
19
+ @configurations = []
20
+ @default_tasks = []
21
+ @project_name = project_name
22
+ @project_folder = File.expand_path(project_folder)
23
+ @output_folder = File.expand_path(File.join(@project_folder, MTBuild.default_output_folder))
24
+ configuration_block.call(self) if configuration_block
25
+
26
+ namespace @project_name do
27
+ @configurations.each do |configuration|
28
+ configuration.configure_tasks()
29
+ end
30
+ end
31
+
32
+ # If there is no active workspace, set up any registered default project tasks
33
+ task :default => @default_tasks unless Workspace.have_workspace?
34
+ end
35
+
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
39
+ end
40
+
41
+ # Set the project's output folder.
42
+ def set_output_folder(output_folder)
43
+ @output_folder = File.expand_path(File.join(@project_folder, output_folder))
44
+ end
45
+
46
+ # Returns the effective output folder. If a workspace exists, this will
47
+ # return the workspace's output folder. If not, it will return the
48
+ # project's output folder.
49
+ 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)
54
+ else
55
+ return @output_folder
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def add_configuration(configuration_name, configuration)
62
+ end
63
+
64
+ def create_configuration_tasks(configuration_name, configuration)
65
+ check_configuration(configuration_name, configuration)
66
+ end
67
+
68
+ include Rake::DSL
69
+ end
70
+
71
+ end
@@ -0,0 +1,49 @@
1
+ module MTBuild
2
+ require 'mtbuild/compiled_configuration'
3
+
4
+ # Use this class to create static library configurations. You won't typically
5
+ # instantiate this directly. Instead, the StaticLibraryProject.add_configuration
6
+ # method will create this for you.
7
+ class StaticLibraryConfiguration < CompiledConfiguration
8
+
9
+ # API header location for the static library
10
+ attr_reader :api_headers
11
+
12
+ def initialize(project_name, project_folder, output_folder, configuration_name, configuration)
13
+ super
14
+ @api_headers = Utils.expand_folder_list(configuration.fetch(:api_headers, []), @project_folder)
15
+ end
16
+
17
+ # Create the actual Rake tasks that will perform the configuration's work
18
+ def configure_tasks
19
+ super
20
+ all_object_files = []
21
+ all_object_folders = []
22
+ @toolchains.each do |toolchain, sources|
23
+ toolchain.add_include_paths(@api_headers)
24
+ object_files, object_folders = toolchain.create_compile_tasks(sources)
25
+ all_object_files |= object_files
26
+ all_object_folders |= object_folders
27
+ end
28
+
29
+ library_files, library_folders = @default_toolchain.create_static_library_tasks(all_object_files, @project_name)
30
+ dependencies = @dependencies+all_object_folders+library_folders+library_files
31
+
32
+ desc "Build library '#{@project_name}' with configuration '#{@configuration_name}'"
33
+ new_task = static_library_task @configuration_name => dependencies do |t|
34
+ puts "built library #{t.name}."
35
+ @tests.each do |test|
36
+ if Rake::Task.task_defined? test
37
+ Rake::Task[test].invoke
38
+ else
39
+ $stderr.puts "warning: Skipping unknown test '#{test}'"
40
+ end
41
+ end
42
+ end
43
+ new_task.api_headers = @api_headers
44
+ new_task.library_files = library_files
45
+ end
46
+
47
+ end
48
+
49
+ end