diff --git a/src/compiler/crystal/command.cr b/src/compiler/crystal/command.cr index 1180485b591edb4cf070a338fad039dd23ba5f18..115367bbb2de044b33f5d78095030ca1e82a6752 100644 --- a/src/compiler/crystal/command.cr +++ b/src/compiler/crystal/command.cr @@ -4,6 +4,7 @@ module Crystal::Command Command:\n \ build compile program file\n \ browser open an http server to browse program file\n \ + deps install project dependencies\n \ eval eval code\n \ hierarchy show type hierarchy\n \ run (default) compile and run program file\n \ @@ -25,6 +26,9 @@ module Crystal::Command when "browser" == command options.shift browser options + when "deps".starts_with?(command) + options.shift + deps options when "eval".starts_with?(command) options.shift eval options @@ -113,6 +117,17 @@ module Crystal::Command execute output_filename, arguments end + private def self.deps(options) + compiler = Compiler.new + sources = gather_sources(["Projectfile"]) + sources.insert 0, Compiler::Source.new("require", %(require "project")) + + output_filename = tempfile "deps" + + result = compiler.compile sources, output_filename + execute output_filename, options + end + private def self.types(options) result = compile_no_build "types", options Crystal.print_types result.original_node diff --git a/src/project/dependency.cr b/src/project/dependency.cr new file mode 100644 index 0000000000000000000000000000000000000000..9459e72a560f872b27366e3ccbcf55bfa9f47759 --- /dev/null +++ b/src/project/dependency.cr @@ -0,0 +1,5 @@ +abstract class Dependency + abstract def install + property locked_version + property! name +end diff --git a/src/project/dsl.cr b/src/project/dsl.cr new file mode 100644 index 0000000000000000000000000000000000000000..f607544e0e85af74724a5b604c1bfbc6f0d25725 --- /dev/null +++ b/src/project/dsl.cr @@ -0,0 +1,14 @@ +class Project + class DSL::Deps + def initialize(@project) + end + + def github(repository) + @project.dependencies << GitHubDependency.new(repository) + end + end +end + +def deps + with Project::DSL::Deps.new(Project::INSTANCE) yield +end diff --git a/src/project/github_dependency.cr b/src/project/github_dependency.cr new file mode 100644 index 0000000000000000000000000000000000000000..964818dbfb6be1a02d574218c8916e5337f8aebf --- /dev/null +++ b/src/project/github_dependency.cr @@ -0,0 +1,30 @@ +class GitHubDependency < Dependency + def initialize(repo) + unless repo =~ /(.*)\/(.*)/ + raise ProjectError.new("Invalid GitHub repository definition: #{repo}") + end + + @author = $1 + @name = @repository = $2 + @target_dir = ".deps/#{@repository}" + end + + def install + unless Dir.exists?(@target_dir) + `git clone git@github.com:#{@author}/#{@repository}.git #{@target_dir}` + end + `ln -sf ../#{@target_dir}/src libs/#{@repository}` + + if @locked_version && + if current_version != @locked_version + `git -C #{@target_dir} checkout -q #{@locked_version}` + end + else + @locked_version = current_version + end + end + + def current_version + `git -C #{@target_dir} rev-parse HEAD`.chomp + end +end diff --git a/src/project/project.cr b/src/project/project.cr new file mode 100644 index 0000000000000000000000000000000000000000..35a4ff9e59294890831047414b0195a7bf62be3b --- /dev/null +++ b/src/project/project.cr @@ -0,0 +1,50 @@ +require "json" +require "./*" + +class Project + INSTANCE = Project.new + property dependencies + + def initialize + @dependencies = [] of Dependency + end + + def install_deps + # Prepare required directories + Dir.mkdir_p ".deps" + Dir.mkdir_p "libs" + + # Load lockfile + if File.exists?(".deps.lock") + lock = Json.parse(File.read(".deps.lock")) as Hash + @dependencies.each do |dep| + if locked_version = lock[dep.name]? + dep.locked_version = locked_version as String + end + end + end + + # Install al dependencies + @dependencies.each &.install + + # Save lockfile + lock = {} of String => String + @dependencies.each do |dep| + lock[dep.name] = dep.locked_version.not_nil! + end + File.write(".deps.lock", lock.to_json) + end +end + +class ProjectError < Exception +end + +redefine_main do |main| + begin + {{main}} + rescue ex : ProjectError + puts ex.message + exit 1 + end + Project::INSTANCE.install_deps +end