From 2d8bed4565c4cb05bdb88c25540f971b6b58f0c9 Mon Sep 17 00:00:00 2001 From: taicsuzu Date: Tue, 15 Nov 2016 00:37:11 +0900 Subject: [PATCH 1/7] parse build command --- src/cli.cr | 5 ++++- src/commands/build.cr | 36 ++++++++++++++++++++++++++++++++++++ src/commands/command.cr | 7 ++++--- src/spec.cr | 20 ++++++++++++++++++++ src/target.cr | 17 +++++++++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 src/commands/build.cr create mode 100644 src/target.cr diff --git a/src/cli.cr b/src/cli.cr index 8a3db14f..5805b665 100644 --- a/src/cli.cr +++ b/src/cli.cr @@ -10,6 +10,7 @@ module Shards #puts " info " puts " init" puts " install" + puts " build" puts " list" puts " prune" #puts " search " @@ -52,7 +53,9 @@ module Shards # Commands::Search.run(args[1]) when "update" Commands::Update.run(path) - #Commands.update(*args[1 .. -1]) + #Commands.update(*args[1 .. -1]) + when "build" + Commands::Build.run(path, args.size > 1 ? args[1] : nil) else display_help_and_exit(opts) end diff --git a/src/commands/build.cr b/src/commands/build.cr new file mode 100644 index 00000000..d02decfd --- /dev/null +++ b/src/commands/build.cr @@ -0,0 +1,36 @@ +require "./command" + +module Shards + module Commands + class Build < Command + def run + # Return if 'crystal' command is not installed + return unless crystal_is_installed? + + name = @sub.nil? ? "default" : @sub + + if name == "all" + Shards.logger.info "Build all targets" + manager.spec.targets.each do |target| + build target + end + else + target = manager.spec.targets.find{ |t| t.name == name } + raise Error.new("Target \'#{name}\' is not found") if target.nil? + build target + end + end + + def build(target) + Shards.logger.info "Target: #{target.name}" + Shards.logger.info " cmd: #{target.cmd}" + # git.cr L232 + end + + def crystal_is_installed? + raise Error.new("\'crystal\' is not installed") unless system("which crystal > /dev/null 2>&1") + true + end + end + end +end diff --git a/src/commands/command.cr b/src/commands/command.cr index 34795e16..ff78c524 100644 --- a/src/commands/command.cr +++ b/src/commands/command.cr @@ -5,13 +5,14 @@ require "../spec" module Shards abstract class Command getter path : String + getter sub : String | Nil getter spec_path : String getter lockfile_path : String @spec : Spec? @locks : Array(Dependency)? - def initialize(path) + def initialize(path, @sub = nil) if File.directory?(path) @path = path @spec_path = File.join(path, SPEC_FILENAME) @@ -24,8 +25,8 @@ module Shards abstract def run - def self.run(path) - new(path).run + def self.run(path, sub = nil) + new(path, sub).run end def spec diff --git a/src/spec.cr b/src/spec.cr index 39651a65..36d6db93 100644 --- a/src/spec.cr +++ b/src/spec.cr @@ -3,6 +3,7 @@ require "yaml" require "./config" require "./dependency" require "./errors" +require "./target" module Shards class Spec @@ -94,6 +95,21 @@ module Shards read_mapping(pull) { dependency[pull.read_scalar] = pull.read_scalar } development_dependencies << dependency end + when "targets" + read_mapping(pull) do + target = Target.new(pull.read_scalar) + read_mapping(pull) do + case pull.read_scalar + when "main" + target.main = pull.read_scalar + when "options" + read_sequence(pull) do + target.options.push(pull.read_scalar) + end + end + end + targets << target + end when "libraries" read_mapping(pull) do libraries << Library.new(pull) @@ -140,6 +156,10 @@ module Shards @development_dependencies ||= [] of Dependency end + def targets + @targets ||= [] of Target + end + def libraries @libraries ||= [] of Library end diff --git a/src/target.cr b/src/target.cr new file mode 100644 index 00000000..b4406665 --- /dev/null +++ b/src/target.cr @@ -0,0 +1,17 @@ +module Shards + class Target + getter name : String + property main : String + property options : Array(String) + + def initialize(@name) + super() + @main = "" + @options = [] of String + end + + def cmd + "crystal build #{@main} #{@options.join(" ")}" + end + end +end From 1cb49b5219e153ad5e2518f78bc154fd5b29ad44 Mon Sep 17 00:00:00 2001 From: taicsuzu Date: Tue, 15 Nov 2016 15:45:52 +0900 Subject: [PATCH 2/7] added the feature --- src/commands/build.cr | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/commands/build.cr b/src/commands/build.cr index d02decfd..23f4b3b2 100644 --- a/src/commands/build.cr +++ b/src/commands/build.cr @@ -5,29 +5,30 @@ module Shards class Build < Command def run # Return if 'crystal' command is not installed - return unless crystal_is_installed? + return unless has_crystal_command? - name = @sub.nil? ? "default" : @sub + @sub ||= "default" - if name == "all" - Shards.logger.info "Build all targets" + if @sub == "all" manager.spec.targets.each do |target| build target end else - target = manager.spec.targets.find{ |t| t.name == name } - raise Error.new("Target \'#{name}\' is not found") if target.nil? + target = manager.spec.targets.find{ |t| t.name == @sub } + raise Error.new("Target \'#{@sub}\' is not found") if target.nil? build target end end def build(target) - Shards.logger.info "Target: #{target.name}" - Shards.logger.info " cmd: #{target.cmd}" - # git.cr L232 + Shards.logger.info "Building: #{target.name}" + + error = MemoryIO.new + status = Process.run("/bin/sh", input: MemoryIO.new(target.cmd), output: nil, error: error) + raise Error.new("#{error.to_s}") unless status.success? end - def crystal_is_installed? + def has_crystal_command? raise Error.new("\'crystal\' is not installed") unless system("which crystal > /dev/null 2>&1") true end From 5f56587da7d92e8d78878360927ee0082962371d Mon Sep 17 00:00:00 2001 From: taicsuzu Date: Wed, 16 Nov 2016 00:09:06 +0900 Subject: [PATCH 3/7] added tests --- test/integration/build_test.cr | 75 ++++++++++++++++++++++++++++++++++ test/support/cli.cr | 19 +++++++++ 2 files changed, 94 insertions(+) create mode 100644 test/integration/build_test.cr diff --git a/test/integration/build_test.cr b/test/integration/build_test.cr new file mode 100644 index 00000000..00d9fb10 --- /dev/null +++ b/test/integration/build_test.cr @@ -0,0 +1,75 @@ +require "../integration_helper" + +class BuildCommandTest < Minitest::Test + + def test_succeeds_for_default + metadata = { + targets: { default: { main: "mock.cr" } } + } + + with_shard(metadata) do + Dir.cd(application_path) do + File.open "mock.cr", "w" + run "shards build" + assert File.exists?(File.join(application_path, "mock")) + end + end + end + + def test_succeeds_for_default_with_options + metadata = { + targets: { default: { main: "mock.cr", options: ["--release"] } } + } + + with_shard(metadata) do + Dir.cd(application_path) do + File.open "mock.cr", "w" + run "shards build" + assert File.exists?(File.join(application_path, "mock")) + end + end + end + + def test_succeeds_for_specified_target + metadata = { + targets: { mock: { main: "mock.cr" } } + } + + with_shard(metadata) do + Dir.cd(application_path) do + File.open "mock.cr", "w" + run "shards build mock" + assert File.exists?(File.join(application_path, "mock")) + end + end + end + + def test_succeeds_for_all_targets + metadata = { targets: { + mock1: { main: "mock1.cr" }, + mock2: { main: "mock2.cr" } + } } + + with_shard(metadata) do + Dir.cd(application_path) do + File.open "mock1.cr", "w" + File.open "mock2.cr", "w" + run "shards build all" + assert File.exists?(File.join(application_path, "mock1")) + assert File.exists?(File.join(application_path, "mock2")) + end + end + end + + def test_fails_when_target_is_missing + metadata = { targets: { mock: { main: "mock.cr" } } } + + with_shard(metadata) do + Dir.cd(application_path) do + File.open "mock.cr", "w" + ex = assert_raises(FailedCommand) { run "shards build mock_fake" } + assert_match "\e[31mTarget\e[0m 'mock_fake' is not found\n", ex.stdout + end + end + end +end diff --git a/test/support/cli.cr b/test/support/cli.cr index 92b3885c..262356fb 100644 --- a/test/support/cli.cr +++ b/test/support/cli.cr @@ -54,6 +54,25 @@ module Shards else yml << value end + elsif key.to_s == "targets" + yml << "targets:\n" + value.each do |target, info| + yml << " " << target.to_s << ":\n" + info.each_key do |info_key| + case info_key + when :main + yml << " main: " << info[info_key].inspect << "\n" + when :options + yml << " options:\n" + options = info[info_key] + if options.is_a?(Array(String)) + options.each do |option| + yml << " - " << option.inspect << "\n" + end + end + end + end + end end end end From 765d10b565f67c9473d6b2ce46236aae24f234ca Mon Sep 17 00:00:00 2001 From: taicsuzu Date: Wed, 16 Nov 2016 22:22:38 +0900 Subject: [PATCH 4/7] remove options from shard.yml --- src/cli.cr | 5 +-- src/commands/build.cr | 59 +++++++++++++++++++++++++++------- src/spec.cr | 14 +++----- src/target.cr | 12 ++----- test/integration/build_test.cr | 59 ++++++++++++++++------------------ test/support/cli.cr | 19 +++-------- 6 files changed, 87 insertions(+), 81 deletions(-) diff --git a/src/cli.cr b/src/cli.cr index 5805b665..ecfb91f4 100644 --- a/src/cli.cr +++ b/src/cli.cr @@ -10,7 +10,7 @@ module Shards #puts " info " puts " init" puts " install" - puts " build" + puts " build [targets] [options]" puts " list" puts " prune" #puts " search " @@ -55,7 +55,8 @@ module Shards Commands::Update.run(path) #Commands.update(*args[1 .. -1]) when "build" - Commands::Build.run(path, args.size > 1 ? args[1] : nil) + Commands::Build.set_args(args[1..(args.size-1)]) if args.size > 1 + Commands::Build.run(path) else display_help_and_exit(opts) end diff --git a/src/commands/build.cr b/src/commands/build.cr index 23f4b3b2..f325b001 100644 --- a/src/commands/build.cr +++ b/src/commands/build.cr @@ -3,34 +3,69 @@ require "./command" module Shards module Commands class Build < Command - def run - # Return if 'crystal' command is not installed - return unless has_crystal_command? - @sub ||= "default" + @@args = [] of String + + # command line arguments + @targets = [] of String + @options = [] of String - if @sub == "all" + def run + # Install dependencies before the build + Install.run(@path) + # Parse to find targets and options + parse_args + # mkdir bin + mkdir_bin + if @targets.size == 0 + raise Error.new("Error: No target found in shard.yml") if manager.spec.targets.nil? manager.spec.targets.each do |target| build target end else - target = manager.spec.targets.find{ |t| t.name == @sub } - raise Error.new("Target \'#{@sub}\' is not found") if target.nil? - build target + @targets.each do |name| + target = manager.spec.targets.find{ |t| t.name == name } + raise Error.new("Error: target \'#{name}\' is not found") if target.nil? + build target + end end end + def parse_args + is_option? = false + @@args.each do |arg| + is_option? = true if arg.starts_with?("-") + if is_option? + @options.push(arg) + else + @targets.push(arg) + end + end + end + + def mkdir_bin + bin_path = File.join(@path, "bin") + Dir.mkdir bin_path unless Dir.exists?(bin_path) + end + def build(target) Shards.logger.info "Building: #{target.name}" + args = ["build", target.main] + @options + unless @options.includes?("-o") + args.push("-o") + args.push(File.join("bin", target.name)) + end + error = MemoryIO.new - status = Process.run("/bin/sh", input: MemoryIO.new(target.cmd), output: nil, error: error) + status = Process.run("crystal", + args: args, + output: nil, error: error) raise Error.new("#{error.to_s}") unless status.success? end - def has_crystal_command? - raise Error.new("\'crystal\' is not installed") unless system("which crystal > /dev/null 2>&1") - true + def self.set_args(args : Array(String)) + @@args = args end end end diff --git a/src/spec.cr b/src/spec.cr index 36d6db93..2a92cb68 100644 --- a/src/spec.cr +++ b/src/spec.cr @@ -97,18 +97,12 @@ module Shards end when "targets" read_mapping(pull) do - target = Target.new(pull.read_scalar) + target_name = pull.read_scalar read_mapping(pull) do - case pull.read_scalar - when "main" - target.main = pull.read_scalar - when "options" - read_sequence(pull) do - target.options.push(pull.read_scalar) - end - end + pull.read_next # main: + target = Target.new(target_name, pull.read_scalar) + targets << target end - targets << target end when "libraries" read_mapping(pull) do diff --git a/src/target.cr b/src/target.cr index b4406665..2704f96e 100644 --- a/src/target.cr +++ b/src/target.cr @@ -1,17 +1,9 @@ module Shards class Target getter name : String - property main : String - property options : Array(String) + getter main : String - def initialize(@name) - super() - @main = "" - @options = [] of String - end - - def cmd - "crystal build #{@main} #{@options.join(" ")}" + def initialize(@name, @main) end end end diff --git a/test/integration/build_test.cr b/test/integration/build_test.cr index 00d9fb10..fdd7c5ce 100644 --- a/test/integration/build_test.cr +++ b/test/integration/build_test.cr @@ -2,73 +2,68 @@ require "../integration_helper" class BuildCommandTest < Minitest::Test - def test_succeeds_for_default + def test_succeeds_for_specified_target metadata = { - targets: { default: { main: "mock.cr" } } + targets: { mock: { main: "mock.cr" } } } with_shard(metadata) do Dir.cd(application_path) do File.open "mock.cr", "w" - run "shards build" - assert File.exists?(File.join(application_path, "mock")) + run "shards build mock" + assert File.exists?(File.join(application_path, "bin", "mock")) end end end - def test_succeeds_for_default_with_options + def test_succeeds_for_all_targets metadata = { - targets: { default: { main: "mock.cr", options: ["--release"] } } + targets: { mock1: { main: "mock1.cr" }, + mock2: { main: "mock2.cr" } } } with_shard(metadata) do Dir.cd(application_path) do - File.open "mock.cr", "w" + File.open "mock1.cr", "w" + File.open "mock2.cr", "w" run "shards build" - assert File.exists?(File.join(application_path, "mock")) + assert File.exists?(File.join(application_path, "bin", "mock1")) + assert File.exists?(File.join(application_path, "bin", "mock2")) end end end - def test_succeeds_for_specified_target + def test_succeeds_for_multiple_targets metadata = { - targets: { mock: { main: "mock.cr" } } + targets: { mock1: { main: "mock1.cr" }, + mock2: { main: "mock2.cr" }, + mock3: { main: "mock3.cr" } } } - with_shard(metadata) do - Dir.cd(application_path) do - File.open "mock.cr", "w" - run "shards build mock" - assert File.exists?(File.join(application_path, "mock")) - end - end - end - - def test_succeeds_for_all_targets - metadata = { targets: { - mock1: { main: "mock1.cr" }, - mock2: { main: "mock2.cr" } - } } - with_shard(metadata) do Dir.cd(application_path) do File.open "mock1.cr", "w" File.open "mock2.cr", "w" - run "shards build all" - assert File.exists?(File.join(application_path, "mock1")) - assert File.exists?(File.join(application_path, "mock2")) + File.open "mock3.cr", "w" + run "shards build mock1 mock2" + assert File.exists?(File.join(application_path, "bin", "mock1")) + assert File.exists?(File.join(application_path, "bin", "mock2")) + assert !File.exists?(File.join(application_path, "bin", "mock3")) end end end - def test_fails_when_target_is_missing - metadata = { targets: { mock: { main: "mock.cr" } } } + def test_fails_when_specified_target_is_not_found + metadata = { + targets: { mock: { main: "mock.cr" } } + } with_shard(metadata) do Dir.cd(application_path) do File.open "mock.cr", "w" - ex = assert_raises(FailedCommand) { run "shards build mock_fake" } - assert_match "\e[31mTarget\e[0m 'mock_fake' is not found\n", ex.stdout + ex = assert_raises(FailedCommand) { run "shards --no-color build mock_fake" } + assert_match "E: Error: target 'mock_fake' is not found\n", ex.stdout + assert_empty ex.stderr end end end diff --git a/test/support/cli.cr b/test/support/cli.cr index 262356fb..b512b30a 100644 --- a/test/support/cli.cr +++ b/test/support/cli.cr @@ -56,21 +56,10 @@ module Shards end elsif key.to_s == "targets" yml << "targets:\n" - value.each do |target, info| - yml << " " << target.to_s << ":\n" - info.each_key do |info_key| - case info_key - when :main - yml << " main: " << info[info_key].inspect << "\n" - when :options - yml << " options:\n" - options = info[info_key] - if options.is_a?(Array(String)) - options.each do |option| - yml << " - " << option.inspect << "\n" - end - end - end + unless value.nil? + value.each do |target, info| + yml << " " << target.to_s << ":\n" + yml << " main: " << info[:main].inspect << "\n" end end end From 75d2bec4d308b9f5d6494e5624b5d0b145ad85aa Mon Sep 17 00:00:00 2001 From: taicsuzu Date: Wed, 16 Nov 2016 23:40:24 +0900 Subject: [PATCH 5/7] fixed a test --- test/support/cli.cr | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/support/cli.cr b/test/support/cli.cr index b512b30a..53057380 100644 --- a/test/support/cli.cr +++ b/test/support/cli.cr @@ -56,10 +56,14 @@ module Shards end elsif key.to_s == "targets" yml << "targets:\n" - unless value.nil? + if value.responds_to?(:each) value.each do |target, info| yml << " " << target.to_s << ":\n" - yml << " main: " << info[:main].inspect << "\n" + if info.responds_to?(:each) + info.each do |main, src| + yml << " main: " << src.inspect << "\n" + end + end end end end From 58fc34f585ff9ed060e6b1308979389475f6facc Mon Sep 17 00:00:00 2001 From: taicsuzu Date: Wed, 16 Nov 2016 23:41:21 +0900 Subject: [PATCH 6/7] removed unused @sub --- src/commands/command.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/command.cr b/src/commands/command.cr index ff78c524..e4d587f1 100644 --- a/src/commands/command.cr +++ b/src/commands/command.cr @@ -12,7 +12,7 @@ module Shards @spec : Spec? @locks : Array(Dependency)? - def initialize(path, @sub = nil) + def initialize(path) if File.directory?(path) @path = path @spec_path = File.join(path, SPEC_FILENAME) From e899213ef1c1977834875a84b72ba5ae40915e4a Mon Sep 17 00:00:00 2001 From: taicsuzu Date: Thu, 17 Nov 2016 14:19:11 +0900 Subject: [PATCH 7/7] removed unused variables --- src/commands/build.cr | 4 ++-- src/commands/command.cr | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/commands/build.cr b/src/commands/build.cr index f325b001..bc99a5e6 100644 --- a/src/commands/build.cr +++ b/src/commands/build.cr @@ -17,7 +17,7 @@ module Shards parse_args # mkdir bin mkdir_bin - if @targets.size == 0 + if @targets.empty? raise Error.new("Error: No target found in shard.yml") if manager.spec.targets.nil? manager.spec.targets.each do |target| build target @@ -34,7 +34,7 @@ module Shards def parse_args is_option? = false @@args.each do |arg| - is_option? = true if arg.starts_with?("-") + is_option? = true if arg.starts_with?('-') if is_option? @options.push(arg) else diff --git a/src/commands/command.cr b/src/commands/command.cr index e4d587f1..34795e16 100644 --- a/src/commands/command.cr +++ b/src/commands/command.cr @@ -5,7 +5,6 @@ require "../spec" module Shards abstract class Command getter path : String - getter sub : String | Nil getter spec_path : String getter lockfile_path : String @@ -25,8 +24,8 @@ module Shards abstract def run - def self.run(path, sub = nil) - new(path, sub).run + def self.run(path) + new(path).run end def spec