Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a feature shards build #136

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/cli.cr
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Shards
#puts " info <package>"
puts " init"
puts " install"
puts " build"
puts " list"
puts " prune"
#puts " search <query>"
Expand Down Expand Up @@ -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
Expand Down
37 changes: 37 additions & 0 deletions src/commands/build.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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"

if @sub == "all"
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
end
end

def build(target)
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 has_crystal_command?
raise Error.new("\'crystal\' is not installed") unless system("which crystal > /dev/null 2>&1")
true
end
end
end
end
7 changes: 4 additions & 3 deletions src/commands/command.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ require "../spec"
module Shards
abstract class Command
getter path : String
getter sub : String | Nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused?

getter spec_path : String
getter lockfile_path : String

@spec : Spec?
@locks : Array(Dependency)?

def initialize(path)
def initialize(path, @sub = nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is sub? Can we avoid it?

if File.directory?(path)
@path = path
@spec_path = File.join(path, SPEC_FILENAME)
Expand All @@ -24,8 +25,8 @@ module Shards

abstract def run

def self.run(path)
new(path).run
def self.run(path, sub = nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused change? It should be rolledback probably.

new(path, sub).run
end

def spec
Expand Down
20 changes: 20 additions & 0 deletions src/spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ require "yaml"
require "./config"
require "./dependency"
require "./errors"
require "./target"

module Shards
class Spec
Expand Down Expand Up @@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid options for now; they could be passed as options to the command for example:

$ shards build --release

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)
Expand Down Expand Up @@ -140,6 +156,10 @@ module Shards
@development_dependencies ||= [] of Dependency
end

def targets
@targets ||= [] of Target
end

def libraries
@libraries ||= [] of Library
end
Expand Down
17 changes: 17 additions & 0 deletions src/target.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Shards
class Target
getter name : String
property main : String
property options : Array(String)

def initialize(@name)
super()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed: there are no superclass.

@main = ""
@options = [] of String
end

def cmd
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about shell_command? Thought the model shouldn't have to deal with the invocation. Command::Build should be the one generating the command.

"crystal build #{@main} #{@options.join(" ")}"
end
end
end
75 changes: 75 additions & 0 deletions test/integration/build_test.cr
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please pass --no-color to the command.

end
end
end
end
19 changes: 19 additions & 0 deletions test/support/cli.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down