environment/lib/make.rb
2025-08-01 20:11:48 +02:00

187 lines
No EOL
5.2 KiB
Ruby

require 'yaml'
require 'tmpdir'
require 'system'
require 'ostruct'
require 'fileutils'
require 'open3'
# make for: the user, system, package
# as regular user, if dependencies provided
# user: $HOME/.local
# as super user
# system: /
# package: /pkg/$name/$version/
module Make
def self.rostruct(obj)
case obj
when Hash
OpenStruct.new(obj.transform_values { |v| rostruct(v) })
when Array
obj.map { |v| rostruct(v) }
else
obj
end
end
class Context
attr_accessor :name, :use_cache, :environment, :steps, :packages, :repository
attr_accessor :target
def initialize(options: OpenStruct.new)
@target = options.target || :user
@name = options.name
@use_cache = options.use_cache || false
#System.detect_os
# rbenv: brew install rbenv && rbenv install 3.0.0
# asdf: asdf install ruby 3.0.0
makefile_path = "#{ENV["DAT_ROOT"]}/recipes/#{@name}.yml"
unless File.file?(makefile_path)
makefile_path = "#{ENV["DAT_ROOT"]}/recipes/#{@name}/#{System.detect_os}.yml"
end
puts "recipe at: #{makefile_path}"
makefile = YAML.load_file(makefile_path)
# puts makefile_path
# puts makefile
@packages = makefile["packages"] || []
@repository = Make.rostruct(makefile["repository"] || OpenStruct.new)
@steps = makefile["steps"] || []
@environment = ENV.to_h.merge(
"PREFIX" => get_prefix
)
environment = makefile["environment"]
if environment != nil
@environment = environment.merge(@environment)
end
end
def get_prefix
case @target
when :user
"#{ENV["HOME"]}/.local"
when :package
"/pkg/#{@name}/#{@repository.branch}"
when :system
"/"
end
end
# Root project directory
def rpd
puts @repository
if @use_cache
path = "#{ENV["HOME"]}/.cache/dat/build/#{@name}/#{@repository.branch}"
FileUtils.mkdir_p(path)
yield path
else
Dir.mktmpdir do |tmp_path|
yield tmp_path
end
end
end
def local_repo
"#{ENV["HOME"]}/.cache/dat/repo/#{@name}.git"
end
def to_s
vars = instance_variables.map do |var|
"#{var.to_s.delete('@')}: #{instance_variable_get(var).inspect}"
end
"Context(#{vars.join(', ')})"
end
end
class Builder
attr_accessor :context, :cwd
def initialize(context)
@context = context
end
def execute(command)
if command.strip.start_with?("cd ")
eval_cmd = command.sub(/^cd /, 'echo ')
# new_dir = `#{eval_cmd}`.strip
new_dir, stderr, status = Open3.capture3(@context.environment, eval_cmd)
begin
puts "Dir: #{Dir.pwd}"
# new_dir = File.expand_path(new_dir)
new_dir = new_dir.strip
puts "Exists #{new_dir} => #{Dir.exist?(new_dir)}"
Dir.chdir(new_dir)
puts "Changed directory to #{Dir.pwd}"
rescue Errno::ENOENT
puts "Directory not found: #{new_dir}"
end
else
system(@context.environment, command)
end
end
def build
install
@context.rpd do | path |
@cwd = path
checkout
puts "path: #{path}"
Dir.chdir(path)
@context.steps.each do |command|
# system(env, command)
execute(command)
# need to be refreshed
# rehash # hash -r # https://chatgpt.com/c/6880b3d6-b190-8330-9623-0458254d2881
end
end
end
def checkout
repo_path = @context.local_repo
repo_url = @context.repository.url
branch = @context.repository.branch
puts "Local bare git repo path: #{repo_path}"
if Dir.exist?(repo_path) && !Dir.empty?(repo_path)
puts "Bare repo exists, fetching updates..."
Dir.chdir(repo_path) do
system("git fetch origin")
end
else
puts "Cloning bare repository..."
FileUtils.mkdir_p(repo_path)
system("git clone --bare #{repo_url} #{repo_path}")
end
system("git --git-dir=#{repo_path} --work-tree=#{@cwd} checkout -f #{branch}")
end
def install
System.install(context.packages)
end
end
# dat make -t pkg --cache --name dry-run
# dat make --name dry-run
def self.command(options)
context = Context.new(options: options)
builder = Builder.new(context)
builder.build
end
end