Skip to content

Commit

Permalink
Merge pull request #318 from savi-lang/add/windows
Browse files Browse the repository at this point in the history
Add support for (cross-compiling to) Windows.
  • Loading branch information
jemc authored May 5, 2022
2 parents 140d3ce + 8362356 commit 4b03323
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ $(eval $(call MAKE_VAR_CACHE_FOR,LLVM_STATIC_RELEASE_URL))

# Specify where to download our pre-built runtime bitcode from.
# This needs to get bumped explicitly here when we do a new runtime build.
RUNTIME_BITCODE_RELEASE_URL?=https://github.com/savi-lang/runtime-bitcode/releases/download/20220206
RUNTIME_BITCODE_RELEASE_URL?=https://github.com/savi-lang/runtime-bitcode/releases/download/v0.20220505.0
$(eval $(call MAKE_VAR_CACHE_FOR,RUNTIME_BITCODE_RELEASE_URL))

# This is the path where we look for the LLVM pre-built static libraries to be,
Expand Down
4 changes: 4 additions & 0 deletions main.cr
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module Savi
option "--llvm-ir", desc: "Write generated LLVM IR to a file", type: Bool, default: false
option "--llvm-keep-fns", desc: "Don't allow LLVM to remove functions from the output", type: Bool, default: false
option "--print-perf", desc: "Print compiler performance info", type: Bool, default: false
option "-X", "--cross-compile=TRIPLE", desc: "Cross compile to the given target triple"
option "-C", "--cd=DIR", desc: "Change the working directory"
option "-p NAME", "--pass=NAME", desc: "Name of the compiler pass to target"
run do |opts, args|
Expand All @@ -33,6 +34,7 @@ module Savi
options.llvm_keep_fns = true if opts.llvm_keep_fns
options.auto_fix = true if opts.fix
options.target_pass = Savi::Compiler.pass_symbol(opts.pass) if opts.pass
options.cross_compile = opts.cross_compile.not_nil! if opts.cross_compile
Dir.cd(opts.cd.not_nil!) if opts.cd
Cli.compile options, opts.backtrace
end
Expand Down Expand Up @@ -111,6 +113,7 @@ module Savi
option "--llvm-ir", desc: "Write generated LLVM IR to a file", type: Bool, default: false
option "--llvm-keep-fns", desc: "Don't allow LLVM to remove functions from the output", type: Bool, default: false
option "--print-perf", desc: "Print compiler performance info", type: Bool, default: false
option "-X", "--cross-compile=TRIPLE", desc: "Cross compile to the given target triple"
option "-C", "--cd=DIR", desc: "Change the working directory"
run do |opts, args|
options = Savi::Compiler::Options.new(
Expand All @@ -122,6 +125,7 @@ module Savi
options.llvm_keep_fns = true if opts.llvm_keep_fns
options.auto_fix = true if opts.fix
options.manifest_name = args.name.not_nil! if args.name
options.cross_compile = opts.cross_compile.not_nil! if opts.cross_compile
Dir.cd(opts.cd.not_nil!) if opts.cd
Cli.compile options, opts.backtrace
end
Expand Down
1 change: 1 addition & 0 deletions src/savi/compiler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class Savi::Compiler
property llvm_ir = false
property llvm_keep_fns = false
property auto_fix = false
property cross_compile : String? = nil
property manifest_name : String?
property target_pass : Symbol?

Expand Down
54 changes: 49 additions & 5 deletions src/savi/compiler/binary.cr
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Savi::Compiler::Binary
def run(ctx)
target = Target.new(ctx.code_gen.target_machine.triple)
bin_path = Binary.path_for(ctx)
bin_path += ".exe" if target.windows?

# Compile a temporary binary object file, that we will remove after we
# use it in the linker invocation to create the final binary.
Expand All @@ -36,6 +37,8 @@ class Savi::Compiler::Binary
link_for_linux_or_bsd(ctx, target, obj_path, bin_path)
elsif target.macos?
link_for_macosx(ctx, target, obj_path, bin_path)
elsif target.windows?
link_for_windows(ctx, target, obj_path, bin_path)
else
raise NotImplementedError.new(target.inspect)
end
Expand Down Expand Up @@ -75,7 +78,9 @@ class Savi::Compiler::Binary

# Set up the main library paths.
# TODO: Support overriding (supplementing?) this via the `SDK_ROOT` env var.
each_sysroot_lib_path(target) { |lib_path| link_args << "-L#{lib_path}" }
each_sysroot_lib_path(ctx, target) { |lib_path|
link_args << "-L#{lib_path}"
}

# Link the main system libraries.
link_args << "-lSystem"
Expand All @@ -94,6 +99,29 @@ class Savi::Compiler::Binary
invoke_linker("mach_o", link_args)
end

# Link a EXE executable for a Windows target.
def link_for_windows(ctx, target, obj_path, bin_path)
link_args = %w{lld-link -nologo -defaultlib:libcmt -defaultlib:oldnames}

# Set up the main library paths.
each_sysroot_lib_path(ctx, target) { |lib_path|
link_args << "-libpath:#{lib_path}"
}

# Specify the base set of libraries to link to.
link_args << "-defaultlib:libcmt" # always needed
link_args << "-defaultlib:oldnames" # always needed
link_args << "-defaultlib:dbghelp" # used by runtime platform/ponyassert.c
link_args << "-defaultlib:ws2_32" # used by runtime lang/socket.c

# Finally, specify the input object file and the output filename.
link_args << obj_path
link_args << "-out:#{bin_path}"

# Invoke the linker, using the COFF flavor.
invoke_linker("coff", link_args)
end

# Link an ELF executable for a Linux or FreeBSD target.
def link_for_linux_or_bsd(ctx, target, obj_path, bin_path)
link_args = %w{ld.lld}
Expand All @@ -118,7 +146,7 @@ class Savi::Compiler::Binary

# Get the list of lib search paths within the sysroot.
lib_paths = [] of String
each_sysroot_lib_path(target) { |path| lib_paths << path }
each_sysroot_lib_path(ctx, target) { |path| lib_paths << path }
lib_paths.each { |lib_path| link_args << "-L#{lib_path}" }

# Also find the mandatory ceremony objects that all programs need to link.
Expand Down Expand Up @@ -173,11 +201,11 @@ class Savi::Compiler::Binary
end

# Yield each sysroot-based path in which to search for linkable libs/objs.
def each_sysroot_lib_path(target)
def each_sysroot_lib_path(ctx, target)
sysroot = "/" # TODO: Allow user to supply custom sysroot for cross-compile.

yielded_any = false
each_sysroot_lib_glob(target) { |lib_glob|
each_sysroot_lib_glob(ctx, target) { |lib_glob|
Dir.glob(lib_glob) { |lib_path|
next unless Dir.exists?(lib_path)

Expand All @@ -190,13 +218,29 @@ class Savi::Compiler::Binary
end

# Yield each sysroot-based glob used to find paths that exist.
def each_sysroot_lib_glob(target)
def each_sysroot_lib_glob(ctx, target)
# For MacOS, we have just one valid sysroot path, so we can finish early.
if target.macos?
yield "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib"
return
end

# For Windows we only allow cross compiling and require this env var.
if target.windows?
unless ctx.options.cross_compile
raise NotImplementedError.new("Windows is only supported by cross-compiling.")
end

sdk_root = ENV["SDK_ROOT"]?
unless sdk_root
raise NotImplementedError.new("The env var `SDK_ROOT` is required to cross-compile to Windows.")
end

yield "#{sdk_root}/**/x86_64"
yield "#{sdk_root}/**/x64"
return
end

if target.linux?
if target.musl?
if target.x86_64?
Expand Down
4 changes: 4 additions & 0 deletions src/savi/compiler/binary_object.cr
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ class Savi::Compiler::BinaryObject
elsif target.arm64?
return "arm64-apple-macosx"
end
elsif target.windows?
if target.x86_64? && target.msvc?
return "x86_64-unknown-windows-msvc"
end
end

raise NotImplementedError.new(target.inspect)
Expand Down
13 changes: 9 additions & 4 deletions src/savi/compiler/code_gen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,16 @@ class Savi::Compiler::CodeGen
getter bitwidth
getter isize

def initialize(runtime : PonyRT.class | VeronaRT.class = PonyRT)
def initialize(
runtime : PonyRT.class | VeronaRT.class,
options : Compiler::Options
)
LLVM.init_x86
LLVM.init_aarch64
LLVM.init_arm
@target_triple = LLVM.configured_default_target_triple.as(String)
@target_triple = (
options.cross_compile || LLVM.configured_default_target_triple
).as(String)
@target = LLVM::Target.from_triple(@target_triple)
@target_machine = @target.create_target_machine(@target_triple).as(LLVM::TargetMachine)
@llvm = LLVM::Context.new
Expand Down Expand Up @@ -1021,9 +1026,9 @@ class Savi::Compiler::CodeGen
when "is_macos"
gen_bool(target.macos?)
when "is_posix"
gen_bool(true) # TODO: false on windows
gen_bool(!target.windows?)
when "is_windows"
gen_bool(false) # TODO: true on windows
gen_bool(target.windows?)
when "is_ilp32"
gen_bool(abi_size_of(@isize) == 4)
when "is_lp64"
Expand Down
6 changes: 4 additions & 2 deletions src/savi/compiler/context.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ class Savi::Compiler::Context
getter program = Program.new

getter classify = Classify::Pass.new
getter code_gen = CodeGen.new(CodeGen::PonyRT)
getter code_gen_verona = CodeGen.new(CodeGen::VeronaRT)
getter code_gen : CodeGen
getter code_gen_verona : CodeGen
getter completeness = Completeness::Pass.new
getter run = Run.new
getter flow = Flow::Pass.new
Expand Down Expand Up @@ -54,6 +54,8 @@ class Savi::Compiler::Context
getter errors = [] of Error

def initialize(@compiler, @options = Compiler::Options.new, @prev_ctx = nil)
@code_gen = CodeGen.new(CodeGen::PonyRT, @options)
@code_gen_verona = CodeGen.new(CodeGen::VeronaRT, @options)
end

def root_package
Expand Down

0 comments on commit 4b03323

Please sign in to comment.