Compare commits

..
21 Commits
Author SHA1 Message Date
josh 4dc1280f68 Add Util.glob() and use from Script 2026-01-28 11:56:34 -05:00
josh 355014def6 Add Precompile builder 2026-01-28 11:49:52 -05:00
josh af7aad716d Use absolute paths for Cache targets and dependencies 2026-01-28 10:58:01 -05:00
josh 13bd827d57 Store build targets, sources, and user dependencies internally as absolute paths 2026-01-28 10:44:03 -05:00
josh a3d739c472 Add Barrier build test 2026-01-28 08:35:01 -05:00
josh 319432bc61 Add Util.absolute_path() 2026-01-28 08:18:25 -05:00
josh d38cd2e8fb Show focused test count from build tests when focusing 2026-01-28 08:12:58 -05:00
josh 72d2802d1e Colorize build test results similar to RSpec tests 2026-01-28 07:56:36 -05:00
josh e6317ae236 Fix SimpleCov coverage results for multiple invocations from same build test 2026-01-27 23:50:04 -05:00
josh baf4e036fb Fix up dspec task for parallelization 2026-01-27 23:50:04 -05:00
josh 9d6321685e Clean up simplecov reporting 2026-01-27 23:50:04 -05:00
josh 3a5db626ac Parallelize build tests 2026-01-27 23:50:04 -05:00
josh 94a86e3433 Add Barrier builder 2026-01-16 22:54:22 -05:00
josh fc18c9f123 Test phony dependencies 2026-01-16 20:21:13 -05:00
josh 1d020d1aef Update gems 2026-01-15 21:07:18 -05:00
josh 19c5c8cfba Update gems 2025-11-22 00:14:26 -05:00
josh 4fc7ab6b25 v3.3.0 2025-06-10 20:14:15 -04:00
josh 161da80ffe Update CHANGELOG for v3.3.0 2025-06-10 20:13:37 -04:00
josh 8d9f19cb34 Add github workflow to run RScons tests
Fix up some macOS test cases
2025-06-08 20:46:09 -04:00
josh 18a2a075c1 Remove dependency on base64 - close #177 2025-06-08 02:48:03 -04:00
josh 1a280a8994 Update gems 2025-06-07 20:30:48 -04:00
28 changed files with 3943 additions and 3547 deletions

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
name: Run RScons Tests
on:
push:
branches:
- master
pull_request:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
ruby-version: ['2.7', '3.0', '3.4']
steps:
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y gcc gdc ldc clang flex bison
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install gcc ldc flex bison
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
- name: Install dependencies
run: bundle install
- name: Run tests
run: rake all
+1 -1
View File
@@ -7,5 +7,5 @@
/gen/ /gen/
/large_project/ /large_project/
/pkg/ /pkg/
/test/ /test_run/
/yard/ /yard/
+9
View File
@@ -1,3 +1,12 @@
## v3.3.0
### New Features
- Add support for Ruby 3.4.
- Fix up some macOS test cases.
- #177 - Remove dependency on base64
- #175 - Add support for building object files from LLVM assembly sources
## v3.2.0 ## v3.2.0
### New Features ### New Features
+2 -1
View File
@@ -3,7 +3,8 @@ source 'https://rubygems.org'
gem "base64" gem "base64"
gem "rspec" gem "rspec"
gem "rake" gem "rake"
gem "simplecov", "~> 0.15.0" gem "simplecov"
gem "openssl"
if RbConfig::CONFIG["host"]["msys"] if RbConfig::CONFIG["host"]["msys"]
gem "json", "2.1.0" gem "json", "2.1.0"
else else
+29 -22
View File
@@ -1,39 +1,45 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
base64 (0.2.0) base64 (0.3.0)
date (3.4.1) date (3.5.1)
diff-lcs (1.6.0) diff-lcs (1.6.2)
docile (1.1.5) docile (1.4.1)
json (2.10.2) erb (6.0.1)
psych (5.2.3) json (2.18.0)
openssl (4.0.0)
psych (5.3.1)
date date
stringio stringio
rake (13.2.1) rake (13.3.1)
rdoc (6.13.0) rdoc (7.1.0)
erb
psych (>= 4.0.0) psych (>= 4.0.0)
tsort
redcarpet (3.6.1) redcarpet (3.6.1)
rspec (3.13.0) rspec (3.13.2)
rspec-core (~> 3.13.0) rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0) rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0) rspec-mocks (~> 3.13.0)
rspec-core (3.13.3) rspec-core (3.13.6)
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-expectations (3.13.3) rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-mocks (3.13.2) rspec-mocks (3.13.7)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-support (3.13.2) rspec-support (3.13.6)
simplecov (0.15.1) simplecov (0.22.0)
docile (~> 1.1.0) docile (~> 1.1)
json (>= 1.8, < 3) simplecov-html (~> 0.11)
simplecov-html (~> 0.10.0) simplecov_json_formatter (~> 0.1)
simplecov-html (0.10.2) simplecov-html (0.13.2)
stringio (3.1.5) simplecov_json_formatter (0.1.4)
stringio (3.2.0)
syntax (1.2.2) syntax (1.2.2)
yard (0.9.37) tsort (0.2.0)
yard (0.9.38)
PLATFORMS PLATFORMS
ruby ruby
@@ -42,13 +48,14 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
base64 base64
json json
openssl
rake rake
rdoc rdoc
redcarpet redcarpet
rspec rspec
simplecov (~> 0.15.0) simplecov
syntax syntax
yard yard
BUNDLED WITH BUNDLED WITH
2.4.17 2.6.2
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2013-2022 Josh Holtrop Copyright (c) 2013-2025 Josh Holtrop
MIT License MIT License
+28 -7
View File
@@ -8,8 +8,10 @@ end
require "rspec/core/rake_task" require "rspec/core/rake_task"
require "rake/clean" require "rake/clean"
require "fileutils" require "fileutils"
require "simplecov"
require "stringio"
CLEAN.include %w[build_test_run .yardoc yard coverage test] CLEAN.include %w[build_test_run .yardoc yard coverage test_run]
CLOBBER.include %w[dist gen large_project pkg] CLOBBER.include %w[dist gen large_project pkg]
task :build_dist do task :build_dist do
@@ -19,24 +21,43 @@ end
RSpec::Core::RakeTask.new(:spec, :example_string) do |task, args| RSpec::Core::RakeTask.new(:spec, :example_string) do |task, args|
ENV["specs"] = "1" ENV["specs"] = "1"
if args.example_string if args.example_string
ENV["partial_specs"] = "1"
task.rspec_opts = %W[-e "#{args.example_string}" -f documentation] task.rspec_opts = %W[-e "#{args.example_string}" -f documentation]
end end
end end
task :spec => :build_dist
task :spec do task :spec do
ENV.delete("specs") ENV.delete("specs")
end end
task :spec => :build_tests
task :spec do
unless ENV["rscons_dist_specs"]
original_stdout = $stdout
sio = StringIO.new
$stdout = sio
SimpleCov.collate Dir["coverage/.resultset.json", "coverage/bt*/.resultset.json"]
$stdout = original_stdout
sio.string.lines.each do |line|
$stdout.write(line) unless line =~ /Coverage report generated for/
end
end
end
task :build_tests do |task, args|
ENV["specs"] = "1"
sh "ruby -Ilib build_tests/build_tests.rb"
ENV.delete("specs")
end
# dspec task is useful to test the distributable release script, but is not # dspec task is useful to test the distributable release script, but is not
# useful for coverage information. # useful for coverage information.
desc "Dist Specs" desc "Dist Specs"
task :dspec, [:example_string] => :build_dist do |task, args| task :dspec, [:example_string] => :build_dist do |task, args|
FileUtils.mkdir_p("test") FileUtils.rm_rf("test_run")
FileUtils.cp("dist/rscons", "test/rscons.rb") FileUtils.mkdir_p("test_run")
ENV["dist_specs"] = "1" FileUtils.cp("dist/rscons", "test_run/rscons.rb")
ENV["rscons_dist_specs"] = "1"
Rake::Task["spec"].execute(args) Rake::Task["spec"].execute(args)
ENV.delete("dist_specs") Rake::Task["build_tests"].execute(args)
ENV.delete("rscons_dist_specs")
FileUtils.rm_f(Dir.glob(".rscons-*")) FileUtils.rm_f(Dir.glob(".rscons-*"))
end end
File diff suppressed because it is too large. Load diff
+14
View File
@@ -0,0 +1,14 @@
class B < Builder
def run(*args)
puts @target
true
end
end
env do |env|
env.add_builder(B)
env.B("one", File.expand_path("two"))
env.B("two")
env.B("three")
env.depends("two", File.expand_path("three"))
end
+15
View File
@@ -0,0 +1,15 @@
class B < Builder
def run(*args)
puts "B:#{@target}"
true
end
end
env do |env|
env.add_builder(B)
env.B("one")
env.B("two")
env.B("three")
env.Barrier(:bar, %w[two three])
env.depends("one", :bar)
end
+1 -1
View File
@@ -3,5 +3,5 @@ env do |env|
env["CFLAGS"] += %w[-S] env["CFLAGS"] += %w[-S]
env.Object("one.ssss", "one.c", "CPPFLAGS" => ["-DONE"]) env.Object("one.ssss", "one.c", "CPPFLAGS" => ["-DONE"])
env.Object("two.sss", "two.c") env.Object("two.sss", "two.c")
env.Program("two_sources.exe", %w[one.ssss two.sss], "ASFLAGS" => env["ASFLAGS"] + %w[-x assembler]) env.Program("two_sources.exe", %w[one.ssss two.sss], "ASFLAGS" => env["ASFLAGS"] + %w[-x assembler-with-cpp])
end end
@@ -3,7 +3,6 @@ env(echo: :command) do |env|
env.add_build_hook do |builder| env.add_build_hook do |builder|
if builder.name == "Object" && builder.sources.first =~ %r{one\.c} if builder.name == "Object" && builder.sources.first =~ %r{one\.c}
builder.vars["CFLAGS"] << "-O1" builder.vars["CFLAGS"] << "-O1"
builder.sources = ['src/two/two.c']
elsif builder.name == "Object" && builder.target =~ %r{two\.o} elsif builder.name == "Object" && builder.target =~ %r{two\.o}
new_vars = builder.vars.clone new_vars = builder.vars.clone
new_vars["CFLAGS"] << "-O2" new_vars["CFLAGS"] << "-O2"
+15
View File
@@ -0,0 +1,15 @@
class Custom < Rscons::Builder
def run(options)
print_run_message("#{name} #{target}", nil)
true
end
end
env do |env|
env.add_builder(Custom)
env.Custom("t3", :phony1)
env.Custom(:phony1, "t2")
env.Custom("t2", :phony2)
env.Custom(:phony2, "t1")
env.Custom("t1", [])
end
+11
View File
@@ -23,6 +23,7 @@ module Rscons
# Names of the default builders which will be added to all newly created # Names of the default builders which will be added to all newly created
# {Environment} objects. # {Environment} objects.
DEFAULT_BUILDERS = [ DEFAULT_BUILDERS = [
:Barrier,
:Command, :Command,
:Copy, :Copy,
:Directory, :Directory,
@@ -32,6 +33,7 @@ module Rscons
:Lex, :Lex,
:Library, :Library,
:Object, :Object,
:Precompile,
:Preprocess, :Preprocess,
:Program, :Program,
:SharedLibrary, :SharedLibrary,
@@ -66,6 +68,13 @@ module Rscons
target.is_a?(Symbol) target.is_a?(Symbol)
end end
# Generate a random phony target name.
#
# @return [Symbol] Phony target name.
def gen_phony_target
("t" + sprintf("%08x", rand(1_000_000..4_000_000_000))).to_sym
end
# Return the system shell and arguments for executing a shell command. # Return the system shell and arguments for executing a shell command.
# #
# @return [Array<String>] The shell and flag. # @return [Array<String>] The shell and flag.
@@ -136,6 +145,7 @@ require_relative "rscons/builders/mixins/object_deps"
require_relative "rscons/builders/mixins/program" require_relative "rscons/builders/mixins/program"
# default builders # default builders
require_relative "rscons/builders/barrier"
require_relative "rscons/builders/command" require_relative "rscons/builders/command"
require_relative "rscons/builders/copy" require_relative "rscons/builders/copy"
require_relative "rscons/builders/directory" require_relative "rscons/builders/directory"
@@ -143,6 +153,7 @@ require_relative "rscons/builders/disassemble"
require_relative "rscons/builders/lex" require_relative "rscons/builders/lex"
require_relative "rscons/builders/library" require_relative "rscons/builders/library"
require_relative "rscons/builders/object" require_relative "rscons/builders/object"
require_relative "rscons/builders/precompile"
require_relative "rscons/builders/preprocess" require_relative "rscons/builders/preprocess"
require_relative "rscons/builders/program" require_relative "rscons/builders/program"
require_relative "rscons/builders/shared_library" require_relative "rscons/builders/shared_library"
+21 -9
View File
@@ -29,19 +29,27 @@ module Rscons
# @return [String, Symbol] # @return [String, Symbol]
# Target file name. # Target file name.
attr_accessor :target attr_reader :target
# @return [Array<String>] # @return [String, Symbol]
# Source file name(s). # Absolute target file name.
attr_accessor :sources attr_reader :abstarget
# @return [Array<String, Symbol>]
# Source file names.
attr_reader :sources
# @return [Array<String, Symbol>]
# Absolute source file names.
attr_reader :abssources
# @return [Cache] # @return [Cache]
# Cache instance. # Cache instance.
attr_accessor :cache attr_reader :cache
# @return [Environment] # @return [Environment]
# The {Environment} performing the build operation. # The {Environment} performing the build operation.
attr_accessor :env attr_reader :env
# @return [Hash, VarSet] # @return [Hash, VarSet]
# Construction variables used to perform the build operation. # Construction variables used to perform the build operation.
@@ -49,7 +57,7 @@ module Rscons
# @return [Set<String>] # @return [Set<String>]
# Side effect file(s) produced when this builder runs. # Side effect file(s) produced when this builder runs.
attr_accessor :side_effects attr_reader :side_effects
# @return [Integer] # @return [Integer]
# Build step. # Build step.
@@ -71,7 +79,11 @@ module Rscons
# Extra construction variables. # Extra construction variables.
def initialize(options) def initialize(options)
@target = options[:target] @target = options[:target]
@sources = options[:sources] @abstarget = Util.absolute_path(@target)
@sources = Array(options[:sources])
@abssources = @sources.map do |source|
Util.absolute_path(source)
end
@cache = options[:cache] @cache = options[:cache]
@env = options[:env] @env = options[:env]
@vars = options[:vars] @vars = options[:vars]
@@ -103,7 +115,7 @@ module Rscons
# @return [void] # @return [void]
def produces(*side_effects) def produces(*side_effects)
side_effects.each do |side_effect| side_effects.each do |side_effect|
side_effect_expanded = @env.expand(side_effect) side_effect_expanded = Util.absolute_path(@env.expand(side_effect))
@env.register_side_effect(side_effect_expanded) @env.register_side_effect(side_effect_expanded)
@side_effects << side_effect_expanded @side_effects << side_effect_expanded
end end
+5 -5
View File
@@ -27,8 +27,8 @@ module Rscons
# env.Directory("dest") # env.Directory("dest")
# env.Install("dest", "bin") # env.Install("dest", "bin")
# env.Install("dest", "share") # env.Install("dest", "share")
self[builder.target] ||= [] self[builder.abstarget] ||= []
self[builder.target] << builder self[builder.abstarget] << builder
end end
# Return the number of remaining build steps. # Return the number of remaining build steps.
@@ -37,7 +37,7 @@ module Rscons
# The number of remaining build steps. # The number of remaining build steps.
def build_steps_remaining def build_steps_remaining
self.reduce(0) do |result, (target, builders)| self.reduce(0) do |result, (target, builders)|
result + builders.size result + builders.count {|b| !b.is_a?(Rscons::Builders::Barrier)}
end end
end end
@@ -54,7 +54,7 @@ module Rscons
# The next builder to run. # The next builder to run.
def get_next_builder_to_run(targets_still_building) def get_next_builder_to_run(targets_still_building)
to_build = self.find do |target, builders| to_build = self.find do |target, builders|
deps = builders.first.sources + (@build_dependencies[target] || []).to_a deps = builders.first.abssources + (@build_dependencies[target] || []).to_a
# All dependencies must have been built for this target to be ready to # All dependencies must have been built for this target to be ready to
# build. # build.
deps.all? do |dep| deps.all? do |dep|
@@ -79,7 +79,7 @@ module Rscons
# not find a builder to run above, then there might be a circular # not find a builder to run above, then there might be a circular
# dependency introduced by the user. # dependency introduced by the user.
if (self.size > 0) and targets_still_building.empty? if (self.size > 0) and targets_still_building.empty?
raise "Could not find a runnable builder. Possible circular dependency for #{self.keys.first}" raise "Could not find a runnable builder. Possible circular dependency for #{self.first[1].first.target}"
end end
end end
+15
View File
@@ -0,0 +1,15 @@
module Rscons
module Builders
# The Barrier builder does not perform any action. It exists as a builder
# on which to place dependencies to ensure that each of its sources are
# built before any build targets which depend on the barrier build target.
class Barrier < Builder
# Run the builder.
def run(options)
true
end
end
end
end
+29
View File
@@ -0,0 +1,29 @@
module Rscons
module Builders
# The Precompile builder generates .di interface files from .d source files
# for D.
class Precompile < Builder
# Run the builder to produce a build target.
def run(options)
if @command
finalize_command
else
if @sources.find {|s| s.end_with?(*@env.expand_varref("${DSUFFIX}", @vars))}
pcc = @env.expand_varref("${DC}")
if pcc =~ /ldc/
dpc_cmd = "${DPC_CMD:ldc}"
else
dpc_cmd = "${DPC_CMD:gdc}"
end
@vars["_TARGET"] = @target
@vars["_SOURCES"] = @sources
command = @env.build_command(dpc_cmd, @vars)
standard_command("Precompile <source>#{Util.short_format_paths(@sources)}<reset>", command)
end
end
end
end
end
end
+12 -6
View File
@@ -142,12 +142,14 @@ module Rscons
# - each cached dependency file's current checksum matches the checksum # - each cached dependency file's current checksum matches the checksum
# stored in the cache file # stored in the cache file
def up_to_date?(targets, command, deps, env, options = {}) def up_to_date?(targets, command, deps, env, options = {})
deps = deps.map {|dep| Util.absolute_path(dep)}
Array(targets).each do |target| Array(targets).each do |target|
cache_key = get_cache_key(target) abstarget = Util.absolute_path(target)
cache_key = get_cache_key(abstarget)
unless Rscons.phony_target?(target) unless Rscons.phony_target?(abstarget)
# target file must exist on disk # target file must exist on disk
unless File.exist?(target) unless File.exist?(abstarget)
if options[:debug] if options[:debug]
puts "Target #{target} needs rebuilding because it does not exist on disk" puts "Target #{target} needs rebuilding because it does not exist on disk"
end end
@@ -163,9 +165,9 @@ module Rscons
return false return false
end end
unless Rscons.phony_target?(target) unless Rscons.phony_target?(abstarget)
# target must have the same checksum as when it was built last # target must have the same checksum as when it was built last
unless @cache["targets"][cache_key]["checksum"] == lookup_checksum(target) unless @cache["targets"][cache_key]["checksum"] == lookup_checksum(abstarget)
if options[:debug] if options[:debug]
puts "Target #{target} needs rebuilding because it has been changed on disk since being built last" puts "Target #{target} needs rebuilding because it has been changed on disk since being built last"
end end
@@ -202,7 +204,7 @@ module Rscons
end end
# set of user dependencies must match # set of user dependencies must match
user_deps = env.get_user_deps(target) || [] user_deps = env.get_user_deps(abstarget) || []
cached_user_deps = @cache["targets"][cache_key]["user_deps"] || [] cached_user_deps = @cache["targets"][cache_key]["user_deps"] || []
cached_user_deps_fnames = cached_user_deps.map { |dc| dc["fname"] } cached_user_deps_fnames = cached_user_deps.map { |dc| dc["fname"] }
unless user_deps == cached_user_deps_fnames unless user_deps == cached_user_deps_fnames
@@ -247,12 +249,16 @@ module Rscons
# @return [void] # @return [void]
def register_build(targets, command, deps, env, options = {}) def register_build(targets, command, deps, env, options = {})
Array(targets).each do |target| Array(targets).each do |target|
target = Util.absolute_path(target)
target_checksum = target_checksum =
if options[:side_effect] or Rscons.phony_target?(target) if options[:side_effect] or Rscons.phony_target?(target)
"" ""
else else
calculate_checksum(target) calculate_checksum(target)
end end
deps = deps.map do |dep|
Util.absolute_path(dep)
end.uniq
@cache["targets"][get_cache_key(target)] = { @cache["targets"][get_cache_key(target)] = {
"command" => Digest::MD5.hexdigest(command.inspect), "command" => Digest::MD5.hexdigest(command.inspect),
"checksum" => target_checksum, "checksum" => target_checksum,
@@ -43,6 +43,8 @@ module Rscons
"DFLAGS" => [], "DFLAGS" => [],
"DISASM_CMD" => %w[${OBJDUMP} ${DISASM_FLAGS} ${_SOURCES}], "DISASM_CMD" => %w[${OBJDUMP} ${DISASM_FLAGS} ${_SOURCES}],
"DISASM_FLAGS" => %w[--disassemble --source], "DISASM_FLAGS" => %w[--disassemble --source],
"DPC_CMD:ldc" => %w[${DC} -H -Hf ${_TARGET} -o- ${INCPREFIX}${D_IMPORT_PATH} ${DFLAGS} ${_SOURCES}],
"DPC_CMD:gdc" => %w[${DC} -H -Hf ${_TARGET} -fsyntax-only ${INCPREFIX}${D_IMPORT_PATH} ${DFLAGS} ${_SOURCES}],
"DSUFFIX" => %w[.d], "DSUFFIX" => %w[.d],
"D_IMPORT_PATH" => [], "D_IMPORT_PATH" => [],
"INCPREFIX" => "-I", "INCPREFIX" => "-I",
+30 -15
View File
@@ -371,8 +371,8 @@ module Rscons
@builder_sets << build_builder_set @builder_sets << build_builder_set
end end
@builder_sets.last << builder @builder_sets.last << builder
@build_steps += 1 @build_steps += 1 unless builder.is_a?(Rscons::Builders::Barrier)
@build_targets[target] = builder @build_targets[builder.abstarget] = builder
builder builder
else else
super super
@@ -396,8 +396,14 @@ module Rscons
end end
expand(ud) expand(ud)
end end
target = Util.absolute_path(target)
@user_deps[target] ||= [] @user_deps[target] ||= []
@user_deps[target] = (@user_deps[target] + user_deps).uniq user_deps.map! {|ud| Util.absolute_path(ud)}
user_deps.each do |ud|
unless Rscons.phony_target?(ud) || @user_deps[target].include?(ud)
@user_deps[target] << ud
end
end
build_after(target, user_deps) build_after(target, user_deps)
end end
@@ -428,13 +434,13 @@ module Rscons
targets = Array(targets) targets = Array(targets)
prerequisites = Array(prerequisites) prerequisites = Array(prerequisites)
targets.each do |target| targets.each do |target|
target = expand(target) target = Util.absolute_path(expand(target))
@registered_build_dependencies[target] ||= Set.new @registered_build_dependencies[target] ||= Set.new
prerequisites.each do |prerequisite| prerequisites.each do |prerequisite|
if prerequisite.is_a?(Builder) if prerequisite.is_a?(Builder)
prerequisite = prerequisite.target prerequisite = prerequisite.target
end end
prerequisite = expand(prerequisite) prerequisite = Util.absolute_path(expand(prerequisite))
@registered_build_dependencies[target] << prerequisite @registered_build_dependencies[target] << prerequisite
end end
end end
@@ -450,9 +456,9 @@ module Rscons
# #
# @return [void] # @return [void]
def produces(target, *side_effects) def produces(target, *side_effects)
target = expand(target) abstarget = Util.absolute_path(expand(target))
@builder_sets.reverse.each do |builder_set| @builder_sets.reverse.each do |builder_set|
if builders = builder_set[target] if builders = builder_set[abstarget]
builders.last.produces(*side_effects) builders.last.produces(*side_effects)
return return
end end
@@ -469,7 +475,7 @@ module Rscons
# @param side_effect [String] # @param side_effect [String]
# Side effect fiel name. # Side effect fiel name.
def register_side_effect(side_effect) def register_side_effect(side_effect)
@side_effects << side_effect @side_effects << Util.absolute_path(side_effect)
end end
# Return the list of user dependencies for a given target. # Return the list of user dependencies for a given target.
@@ -480,6 +486,7 @@ module Rscons
# List of user-specified dependencies for the target, or nil if none were # List of user-specified dependencies for the target, or nil if none were
# specified. # specified.
def get_user_deps(target) def get_user_deps(target)
target = Util.absolute_path(target)
@user_deps[target] @user_deps[target]
end end
@@ -503,10 +510,11 @@ module Rscons
# @return [String] # @return [String]
# Output file name. # Output file name.
def register_dependency_build(target, source, suffix, vars, builder_class) def register_dependency_build(target, source, suffix, vars, builder_class)
target = Util.absolute_path(target)
output_fname = get_build_fname(source, suffix, builder_class) output_fname = get_build_fname(source, suffix, builder_class)
self.__send__(builder_class.name, output_fname, source, vars) self.__send__(builder_class.name, output_fname, source, vars)
@registered_build_dependencies[target] ||= Set.new @registered_build_dependencies[target] ||= Set.new
@registered_build_dependencies[target] << output_fname @registered_build_dependencies[target] << Util.absolute_path(output_fname)
output_fname output_fname
end end
@@ -583,6 +591,7 @@ module Rscons
# @return [Builder, nil] # @return [Builder, nil]
# The {Builder} for target, or +nil+ if none found. # The {Builder} for target, or +nil+ if none found.
def builder_for(target) def builder_for(target)
target = Util.absolute_path(target)
@build_targets[target] @build_targets[target]
end end
@@ -623,7 +632,9 @@ module Rscons
# #
# @return [void] # @return [void]
def run_builder(builder) def run_builder(builder)
builder.build_step ||= get_next_build_step unless builder.is_a?(Rscons::Builders::Barrier)
builder.build_step ||= get_next_build_step
end
case result = builder.run({}) case result = builder.run({})
when Array when Array
result.each do |waititem| result.each do |waititem|
@@ -649,8 +660,10 @@ module Rscons
Cache.instance.register_build(side_effect, nil, [], self, side_effect: true) Cache.instance.register_build(side_effect, nil, [], self, side_effect: true)
@side_effects.delete(side_effect) @side_effects.delete(side_effect)
end end
@build_hooks[:post].each do |build_hook_block| unless builder.is_a?(Rscons::Builders::Barrier)
build_hook_block.call(builder) @build_hooks[:post].each do |build_hook_block|
build_hook_block.call(builder)
end
end end
process_remove_wait(builder) process_remove_wait(builder)
else else
@@ -722,13 +735,15 @@ module Rscons
# If no builder was found to run yet and there are threads available, try # If no builder was found to run yet and there are threads available, try
# to get a runnable builder from the builder set. # to get a runnable builder from the builder set.
targets_still_building = @threads.reduce([]) do |result, (thread, obj)| targets_still_building = @threads.reduce([]) do |result, (thread, obj)|
result << builder_for_thread(thread).target result << builder_for_thread(thread).abstarget
end end
if @builder_sets.size > 0 if @builder_sets.size > 0
if builder = @builder_sets[0].get_next_builder_to_run(targets_still_building) if builder = @builder_sets[0].get_next_builder_to_run(targets_still_building)
builder.vars = @varset.merge(builder.vars) builder.vars = @varset.merge(builder.vars)
@build_hooks[:pre].each do |build_hook_block| unless builder.is_a?(Rscons::Builders::Barrier)
build_hook_block.call(builder) @build_hooks[:pre].each do |build_hook_block|
build_hook_block.call(builder)
end
end end
return run_builder(builder) return run_builder(builder)
end end
+1 -9
View File
@@ -34,15 +34,7 @@ module Rscons
# #
# @return [Array<String>] Paths matching the specified pattern(s). # @return [Array<String>] Paths matching the specified pattern(s).
def glob(*patterns) def glob(*patterns)
require "pathname" Util.glob(*patterns)
patterns.reduce([]) do |result, pattern|
if pattern.end_with?("/**")
pattern += "/"
end
result += Dir.glob(pattern).map do |path|
Pathname.new(path.gsub("\\", "/")).cleanpath.to_s
end
end.sort
end end
# Download a file. # Download a file.
+42
View File
@@ -16,6 +16,21 @@ module Rscons
end end
end end
# Return the absolute path for a given target if not a phony target.
#
# @param path [String, Symbol, nil]
# Given target name.
#
# @return [String, Symbol, nil]
# Absolute path of given target.
def absolute_path(path)
if path.is_a?(String)
File.expand_path(path)
else
path
end
end
# Colorize a builder run message. # Colorize a builder run message.
# #
# @param message [String] # @param message [String]
@@ -141,6 +156,33 @@ module Rscons
result result
end end
# Return a list of paths matching the specified pattern(s).
#
# A pattern can contain a "/**" component to recurse through directories.
# If the pattern ends with "/**" then only the recursive list of
# directories will be returned.
#
# Examples:
# - "src/**": return all directories under "src", recursively (including
# "src" itself).
# - "src/**/*": return all files and directories recursively under the src
# directory.
# - "src/**/*.c": return all .c files recursively under the src directory.
# - "dir/*/": return all directories in dir, but no files.
#
# @return [Array<String>] Paths matching the specified pattern(s).
def glob(*patterns)
require "pathname"
patterns.reduce([]) do |result, pattern|
if pattern.end_with?("/**")
pattern += "/"
end
result += Dir.glob(pattern).map do |path|
Pathname.new(path.gsub("\\", "/")).cleanpath.to_s
end
end.sort
end
# Make a relative path corresponding to a possibly absolute one. # Make a relative path corresponding to a possibly absolute one.
# #
# @param path [String] # @param path [String]
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons module Rscons
# Project version. # Project version.
VERSION = "3.2.0" VERSION = "3.3.0"
end end
+45 -10
View File
@@ -1,7 +1,10 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require "fileutils" require "base64"
require "digest/md5" require "digest/md5"
require "fileutils"
require "stringio"
require "zlib"
if File.read("lib/rscons/version.rb") =~ /VERSION = "(.+)"/ if File.read("lib/rscons/version.rb") =~ /VERSION = "(.+)"/
VERSION = $1 VERSION = $1
@@ -68,11 +71,15 @@ license = File.read("LICENSE.txt").gsub(/^(.*?)$/) do |line|
end end
end end
require "zlib"
require "base64"
compressed_script = Zlib::Deflate.deflate(stripped.join) compressed_script = Zlib::Deflate.deflate(stripped.join)
hash = Digest::MD5.hexdigest(compressed_script)
encoded_compressed_script = Base64.encode64(compressed_script).gsub("\n", "") encoded_compressed_script = Base64.encode64(compressed_script).gsub("\n", "")
hash = Digest::MD5.hexdigest(encoded_compressed_script) encoded_compressed_script_io = StringIO.new(encoded_compressed_script)
commented_encoded_compressed_script = ""
until encoded_compressed_script_io.eof?
line = encoded_compressed_script_io.read(64)
commented_encoded_compressed_script += "##{line}\n"
end
FileUtils.rm_rf(DIST) FileUtils.rm_rf(DIST)
FileUtils.mkdir_p(DIST) FileUtils.mkdir_p(DIST)
@@ -82,25 +89,53 @@ File.open("#{DIST}/#{PROG_NAME}", "wb", 0755) do |fh|
#{license} #{license}
BASE64CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
def base64_decode(s)
out = ""
v = 0
bits = 0
s.each_char do |c|
if cv = BASE64CHARS.index(c)
v = (v << 6) | cv
bits += 6
elsif c == "="
break
end
if bits >= 8
out += (v >> (bits - 8)).chr
v &= 0xFFFFFFFF >> (32 - (bits - 8))
bits -= 8
end
end
out
end
script = File.join(File.dirname(__FILE__), ".rscons-#{VERSION}-#{hash}.rb") script = File.join(File.dirname(__FILE__), ".rscons-#{VERSION}-#{hash}.rb")
unless File.exist?(script) unless File.exist?(script)
if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/ if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/m
require "zlib" require "zlib"
require "base64"
encoded_compressed = $1 encoded_compressed = $1
unescaped_compressed = Base64.decode64(encoded_compressed) compressed = base64_decode(encoded_compressed)
inflated = Zlib::Inflate.inflate(unescaped_compressed) if ENV["rscons_dist_specs"]
require "digest/md5"
if Digest::MD5.hexdigest(compressed) != "#{hash}"
raise "Hash mismatch when decompressing rscons executable"
end
end
inflated = Zlib::Inflate.inflate(compressed)
File.open(script, "wb") do |fh| File.open(script, "wb") do |fh|
fh.write(inflated) fh.write(inflated)
end end
else else
raise "Could not decompress." raise "Error expanding rscons executable"
end end
end end
load script load script
if __FILE__ == $0 if __FILE__ == $0
Rscons::Cli.new.run(ARGV) Rscons::Cli.new.run(ARGV)
end end
#==>#{encoded_compressed_script} #==>
#{commented_encoded_compressed_script}
EOF EOF
end end
File diff suppressed because it is too large. Load diff
-21
View File
@@ -138,27 +138,6 @@ module Rscons
end end
end end
describe "#depends" do
it "records the given dependencies in @user_deps" do
env = Environment.new
env.depends("foo", "bar", "baz")
expect(env.instance_variable_get(:@user_deps)).to eq({"foo" => ["bar", "baz"]})
end
it "records user dependencies only once" do
env = Environment.new
env.instance_variable_set(:@user_deps, {"foo" => ["bar"]})
env.depends("foo", "bar", "baz")
expect(env.instance_variable_get(:@user_deps)).to eq({"foo" => ["bar", "baz"]})
end
it "expands arguments for construction variable references" do
env = Environment.new
env["foo"] = "foo.exe"
env["bar"] = "bar.c"
env.depends("${foo}", "${bar}", "a.h")
expect(env.instance_variable_get(:@user_deps)).to eq({"foo.exe" => ["bar.c", "a.h"]})
end
end
describe "#shell" do describe "#shell" do
it "executes the given shell command and returns the results" do it "executes the given shell command and returns the results" do
env = Environment.new env = Environment.new
+8 -8
View File
@@ -1,8 +1,12 @@
if ENV["dist_specs"] if ENV["rscons_dist_specs"]
require_relative "../test/rscons" require_relative "../test_run/rscons"
else else
require "simplecov" require "simplecov"
class MyFormatter
def format(*args)
end
end
SimpleCov.start do SimpleCov.start do
add_filter "/spec/" add_filter "/spec/"
add_filter "/.bundle/" add_filter "/.bundle/"
@@ -11,14 +15,10 @@ else
else else
command_name "RSpec" command_name "RSpec"
end end
if ENV["dist_specs"] add_filter "test_run/rscons.rb"
add_filter "/bin/"
add_filter "/lib/"
else
add_filter "test/rscons.rb"
end
project_name "Rscons" project_name "Rscons"
merge_timeout 3600 merge_timeout 3600
formatter(MyFormatter)
end end
require "rscons" require "rscons"