Skip to content

Commit b426837

Browse files
authored
Merge pull request #491 from savi-lang/add/prefer-static-link
Add `:prefer static` option for `:ffi_link_lib`.
2 parents c2fb23b + 9b63495 commit b426837

File tree

7 files changed

+84
-40
lines changed

7 files changed

+84
-40
lines changed

core/declarators/declarators.savi

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -356,16 +356,6 @@
356356
:optional
357357
:body optional
358358

359-
:: Declare a link-time dependency on a particular dynamic library, by name.
360-
::
361-
:: This influences the linker arguments that the Savi compiler will use in the
362-
:: linker invocation when building the binary executable for the program.
363-
::
364-
:: For example, declaring `:ffi_link_lib foo` will pass `-lfoo` to the linker.
365-
:declarator ffi_link_lib
366-
:intrinsic
367-
:term name Name
368-
369359
:: Declare a link-time dependency on some files written in C code,
370360
:: which will be compiled and statically linked in the Savi program.
371361
::
@@ -389,6 +379,29 @@
389379
:intrinsic
390380
:term filenames NameList
391381

382+
:: Declare a link-time dependency on a particular dynamic library, by name.
383+
::
384+
:: This influences the linker arguments that the Savi compiler will use in the
385+
:: linker invocation when building the binary executable for the program.
386+
::
387+
:: For example, declaring `:ffi_link_lib foo` will pass `-lfoo` to the linker.
388+
:declarator ffi_link_lib
389+
:intrinsic
390+
:term name Name
391+
:begins ffi_link_lib
392+
393+
:: Declare that the compiler should prefer to link the above library statically.
394+
::
395+
:: This influences the linker arguments that the Savi compiler will use in the
396+
:: linker invocation when building the binary executable for the program.
397+
::
398+
:: If a static version of the library is found, it will be used.
399+
:: Otherwise, the compiler will fall back to specifying dynamic linking for it.
400+
:declarator prefer
401+
:intrinsic
402+
:context ffi_link_lib
403+
:term static enum (static)
404+
392405
:: Declare a binding to an unsafe foreign function (FFI), such as a C function.
393406
::
394407
:: It is common to define such bindings on a dedicated private module, which is

src/savi/compiler/binary.cr

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class Savi::Compiler::Binary
6262

6363
# Link a MachO executable for a MacOSX target.
6464
def link_for_macosx(ctx, target, obj_path, bin_path)
65+
lib_paths = [] of String
6566
link_args = %w{ld64.lld -execute}
6667

6768
# Specify the target architecture.
@@ -72,11 +73,13 @@ class Savi::Compiler::Binary
7273

7374
# Set up explicitly requested library paths.
7475
each_explicit_lib_path(ctx) { |lib_path|
76+
lib_paths << lib_path
7577
link_args << "-L#{lib_path}"
7678
}
7779

7880
# Set up the main library paths.
7981
each_sysroot_lib_path(ctx, target) { |lib_path|
82+
lib_paths << lib_path
8083
link_args << "-L#{lib_path}"
8184
}
8285

@@ -93,7 +96,7 @@ class Savi::Compiler::Binary
9396
link_args << "-lc++" if ctx.link_cpp_files.any?
9497

9598
# Link any additional libraries indicated by user code.
96-
ctx.link_libraries.each { |name| link_args << "-l#{name}" }
99+
ctx.link_libraries.each { |name, kind| add_unix_lib(link_args, lib_paths, name, kind) }
97100

98101
# Finally, specify the input object file and the output filename.
99102
link_args << obj_path
@@ -126,7 +129,7 @@ class Savi::Compiler::Binary
126129
add_windows_lib(link_args, lib_paths, "WS2_32") # used by runtime lang/socket.c
127130

128131
# Link any additional libraries indicated by user code.
129-
ctx.link_libraries.each { |name| add_windows_lib(link_args, lib_paths, name) }
132+
ctx.link_libraries.each { |name, kind| add_windows_lib(link_args, lib_paths, name) }
130133

131134
# Finally, specify the input object file and the output filename.
132135
link_args << obj_path
@@ -136,20 +139,6 @@ class Savi::Compiler::Binary
136139
invoke_linker("coff", link_args)
137140
end
138141

139-
private def add_windows_lib(link_args, lib_paths, name)
140-
if lib_paths.any? { |lib_path|
141-
File.exists?(File.join(lib_path, "#{name}.Lib"))
142-
}
143-
link_args << "-defaultlib:#{name}.Lib"
144-
elsif lib_paths.any? { |lib_path|
145-
File.exists?(File.join(lib_path, "#{name}.lib"))
146-
}
147-
link_args << "-defaultlib:#{name}.lib"
148-
else
149-
link_args << "-defaultlib:#{name.downcase}.lib"
150-
end
151-
end
152-
153142
# Link an ELF executable for a Linux or FreeBSD target.
154143
def link_for_linux_or_bsd(ctx, target, obj_path, bin_path)
155144
link_args = %w{ld.lld}
@@ -172,13 +161,9 @@ class Savi::Compiler::Binary
172161
# Specify the dynamic linker library, based on the target platform.
173162
link_args << "-dynamic-linker" << dynamic_linker_for_linux_or_bsd(target)
174163

175-
# Set up explicitly requested library paths.
176-
each_explicit_lib_path(ctx) { |lib_path|
177-
link_args << "-L#{lib_path}"
178-
}
179-
180-
# Get the list of lib search paths within the sysroot.
164+
# Get the list of lib search paths.
181165
lib_paths = [] of String
166+
each_explicit_lib_path(ctx) { |lib_path| lib_paths << lib_path }
182167
each_sysroot_lib_path(ctx, target) { |path| lib_paths << path }
183168
lib_paths.each { |lib_path| link_args << "-L#{lib_path}" }
184169

@@ -214,7 +199,7 @@ class Savi::Compiler::Binary
214199
end
215200

216201
# Link any additional libraries indicated by user code.
217-
ctx.link_libraries.each { |name| link_args << "-l#{name}" }
202+
ctx.link_libraries.each { |name, kind| add_unix_lib(link_args, lib_paths, name, kind) }
218203

219204
# Finally, specify the input object file and the output filename.
220205
link_args << obj_path
@@ -224,6 +209,32 @@ class Savi::Compiler::Binary
224209
invoke_linker("elf", link_args)
225210
end
226211

212+
private def add_unix_lib(link_args, lib_paths, name, kind)
213+
if kind == :prefer_static
214+
found_lib = maybe_find_in_paths(lib_paths, "lib#{name}.a")
215+
if found_lib
216+
link_args << found_lib
217+
return
218+
end
219+
end
220+
221+
link_args << "-l#{name}"
222+
end
223+
224+
private def add_windows_lib(link_args, lib_paths, name)
225+
if lib_paths.any? { |lib_path|
226+
File.exists?(File.join(lib_path, "#{name}.Lib"))
227+
}
228+
link_args << "-defaultlib:#{name}.Lib"
229+
elsif lib_paths.any? { |lib_path|
230+
File.exists?(File.join(lib_path, "#{name}.lib"))
231+
}
232+
link_args << "-defaultlib:#{name}.lib"
233+
else
234+
link_args << "-defaultlib:#{name.downcase}.lib"
235+
end
236+
end
237+
227238
# Get the path to the dynamic linker library for a Linux or FreeBSD target.
228239
def dynamic_linker_for_linux_or_bsd(target) : String
229240
if target.linux?
@@ -363,12 +374,16 @@ class Savi::Compiler::Binary
363374
# Given a prioritized list of search paths and a file name, find the file.
364375
# Raises an error if the file couldn't be found in any of the paths
365376
def find_in_paths(paths, file_name) : String
377+
result = maybe_find_in_paths(paths, file_name)
378+
raise "failed to find #{file_name}" if !result
379+
result
380+
end
381+
def maybe_find_in_paths(paths, file_name) : String?
366382
paths.each { |path|
367383
file_path = File.join(path, file_name)
368384
return file_path if File.exists?(file_path)
369385
}
370-
371-
raise "failed to find #{file_name}"
386+
nil
372387
end
373388

374389
def invoke_linker(flavor, link_args)

src/savi/compiler/binary_object.cr

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@ class Savi::Compiler::BinaryObject
128128
# so we can mark for linking those libraries that are associated to
129129
# specific functions that come from those libraries.
130130
ctx.link_libraries_by_foreign_function.each { |ffi_name, lib_name|
131-
ctx.link_libraries.add(lib_name) if mod.functions[ffi_name]?
131+
next unless mod.functions[ffi_name]?
132+
next if ctx.link_libraries[lib_name]?
133+
ctx.link_libraries[lib_name] = :dynamic
132134
}
133135

134136
# Strip debug info from the module if requested.

src/savi/compiler/binary_verona.cr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class Savi::Compiler::BinaryVerona
3939
-lc -pthread -ldl -latomic
4040
}
4141

42-
ctx.link_libraries.each do |x|
42+
ctx.link_libraries.each do |x, kind|
4343
link_args << "-l" + x
4444
end
4545

src/savi/compiler/context.cr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class Savi::Compiler::Context
4545

4646
getter link_c_files = Set(String).new
4747
getter link_cpp_files = Set(String).new
48-
getter link_libraries = Set(String).new
48+
getter link_libraries = Hash(String, Symbol).new
4949
getter link_libraries_by_foreign_function = Hash(String, String).new
5050

5151
getter options : Compiler::Options

src/savi/program/declarator/intrinsic.cr

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,9 @@ module Savi::Program::Intrinsic
9494
raise NotImplementedError.new(declarator.name.value)
9595
end
9696
when "ffi_link_lib"
97-
name = terms["name"].as(AST::Identifier)
98-
ctx.link_libraries.add(name.value)
97+
name = terms["name"].as(AST::Identifier).value
98+
ctx.link_libraries[name] = :dynamic
99+
scope.current_ffi_link_lib = name
99100
when "ffi_link_c_files", "ffi_link_cpp_files"
100101
terms["filenames"].as(AST::Group).terms.each { |term|
101102
c_file_path =
@@ -118,6 +119,17 @@ module Savi::Program::Intrinsic
118119
raise NotImplementedError.new(declarator.pretty_inspect)
119120
end
120121

122+
# Declarations within an ffi_link_lib declaration.
123+
when "ffi_link_lib"
124+
case declarator.name.value
125+
when "prefer"
126+
if terms["static"]?
127+
ctx.link_libraries[scope.current_ffi_link_lib] = :prefer_static
128+
end
129+
else
130+
raise NotImplementedError.new(declarator.pretty_inspect)
131+
end
132+
121133
# Declarations within a declarator definition.
122134
when "declarator"
123135
case declarator.name.value

src/savi/program/declarator/scope.cr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class Savi::Program::Declarator::Scope
1919
setter current_declarator : Declarator?
2020
getter! current_declarator_term : Declarator::TermAcceptor
2121
setter current_declarator_term : Declarator::TermAcceptor?
22+
getter! current_ffi_link_lib : String
23+
setter current_ffi_link_lib : String?
2224
getter! current_type : Type
2325
setter current_type : Type?
2426
getter! current_function : Function

0 commit comments

Comments
 (0)