Module: dfmc-c-linker
Author: keith and jonathan
Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc.
All rights reserved.
License: Functional Objects Library Public License Version 1.0
Dual-license: GNU Lesser General Public License
Warranty: Distributed WITHOUT WARRANTY OF ANY KIND
//// C backend Makefile generation ...
define sideways method source-suffix (back-end :: <c-back-end>, t :: <unix-makefile-target>) "c" end;
define sideways method object-suffix (back-end :: <c-back-end>, t :: <unix-makefile-target>) "o" end;
define sideways method source-suffix (back-end :: <c-back-end>, t :: <win32-makefile-target>) "c" end;
define sideways method object-suffix (back-end :: <c-back-end>, t :: <win32-makefile-target>) "obj" end;
define sideways method resource-suffix (back-end :: <c-back-end>, t :: <win32-makefile-target>) "res" end;
define sideways method source-suffix (back-end :: <c-back-end>, t :: <MPW-makefile-target>) "c" end;
define sideways method object-suffix (back-end :: <c-back-end>, t :: <MPW-makefile-target>) "o" end;
define sideways method resource-suffix (back-end :: <c-back-end>, t :: <MPW-makefile-target>) "rsrc" end;
define sideways method output-basename
(back-end :: <c-back-end>, t :: <makefile-target>, basename :: <string>)
=> (basename)
c-output-basename(basename);
end method;
define variable *shortend-basenames* = make(<string-table>);
///---*** NOTE: Should be part of the generic backend code, not here ...
///---*** (Obvious way of using a method on <back-end> would be ambiguous)
define sideways method output-basename
(back-end :: <c-back-end>, t :: <MPW-makefile-target>, basename :: <string>)
=> (basename)
let full-basename = c-output-basename(basename);
if (size(full-basename) + size(".X") > 31)
element(*shortend-basenames*, full-basename, default: #f)
| begin
// Remove characters from the middle...
let full-basename-size = size(full-basename);
let middle = truncate/(full-basename-size, 2);
let excess = full-basename-size + size(".X") - 31;
let short-basename = concatenate(copy-sequence(full-basename, end: middle - excess),
copy-sequence(full-basename, start: middle));
*shortend-basenames*[full-basename] := short-basename
end
else
full-basename
end
end method;
define method library-string (library)
as-lowercase(as(<string>, library.library-description-emit-name))
end method;
define method derived-platform-libraries (t, c-libraries)
#()
end method derived-platform-libraries;
/// If a Win32 executable is linked with the $(guilflags) option, it's a GUI application
/// rather than a console application and will need the Win32Main library to supply an
/// appropriate WinMain function ...
define method derived-platform-libraries (t :: <win32-makefile-target>, c-libraries)
if (any?(method (x) x = "$(guilflags)" end, c-libraries))
#("Win32Main.lib")
else
#()
end
end method derived-platform-libraries;
// These two are define methods and out here rather than local methods
// to work around an emulator bug in #all-keys handling.
define method ldbs-executable (#key executable = #f, #all-keys)
=> (executable :: false-or(<string>))
executable
end method ldbs-executable;
///---*** NOTE: This generic and its methods should probably be moved to the generic backend.
define generic emit-lib-build-commands (t :: <makefile-target>, stream, lib);
define method emit-lib-build-commands (t :: <makefile-target>, stream, lib)
emit-makefile-rule
(t, stream, lib,
map(curry(makefile-variable-ref, t), #("OBJS", "C_OBJS", "C_SRC_OBJS")),
makefile-command(t, "{LINKLIB}%s%s {OBJS} {C_OBJS} {C_SRC_OBJS}",
link-target-separator(t), lib))
end method emit-lib-build-commands;
define method emit-lib-build-commands (t :: <MPW-makefile-target>, stream, lib)
emit-makefile-rule
(t, stream, lib,
map(curry(makefile-variable-ref, t), #("OBJS", "C_OBJS", "C_SRC_OBJS", "RC_OBJS")),
// First build the stub library and generate the exports list ...
makefile-command(t,
"{LINKSTUB}%s%s {RC_OBJS} {OBJS} {C_OBJS} {C_SRC_OBJS}"
" {LIBS} {C_LIBS} {LIBRTLIBS} {LINKER_OPTIONS}",
link-target-separator(t), lib),
// Remove the CodeWarrior runtime exports to avoid duplicate definitions ...
// (NOTE: \<A5> is Option-8 and \<B0> is Option-5)
makefile-command(t,
"StreamEdit -e '/\<A5>__ptmf_null\<B0>/||/\<A5>__vec_longjmp\<B0>/"
"||/\<A5>longjmp\<B0>/||/\<A5>__terminate\<B0>/"
"||/\<A5>__initialize\<B0>/delete' -o {LIBNAME}.exp {LIBNAME}.exp"),
// Rebuild the stub library with the "proper" exports list ...
makefile-command(t,
"{LINKSTUB}%s%s {RC_OBJS} {OBJS} {C_OBJS} {C_SRC_OBJS}"
" {LIBS} {C_LIBS} {LIBRTLIBS} {LINKER_OPTIONS}",
link-target-separator(t), lib),
// Rename the stub library so it won't get overwritten ...
makefile-command(t, "Rename {LIBFILE} {LIBFILE}.stub"),
// Finally build the actual shared library ...
makefile-command(t,
"{LINKLIB}%s%s {RC_OBJS} {OBJS} {C_OBJS} {C_SRC_OBJS}"
" {LIBS} {C_LIBS} {LIBRTLIBS} {LINKER_OPTIONS}",
link-target-separator(t), lib))
end method emit-lib-build-commands;
///---*** NOTE: This generic and its methods should probably be moved to the generic backend.
define generic emit-lib-install-commands
(t :: <makefile-target>, stream, installed-libfile, lib-desc, target);
define method emit-lib-install-commands
(t :: <makefile-target>, stream, installed-libfile, lib-desc, target)
emit-makefile-rule
(t, stream, installed-libfile,
map(curry(makefile-variable-ref, t), #("LIBFILE")),
makefile-command(t, "{UNINSTALL} %s", library-unit-ref(t, lib-desc, target)),
makefile-command(t,
"{INSTALLLIB} %s %s",
makefile-pathname(t, makefile-variable-ref(t, "LIBFILE")),
makefile-pathname
(t, makefile-variable-ref(t, "OPEN_DYLAN_USER_LIB"))))
end method emit-lib-install-commands;
define method emit-lib-install-commands
(t :: <MPW-makefile-target>, stream, installed-libfile, lib-desc, target)
let stub-source
= makefile-pathname(t, concatenate(library-unit-name(t, target), ".stub"));
let stub-destination
= library-unit-ref(t, lib-desc, target);
let shlib-source
= makefile-pathname(t, library-unit-name(t, target));
let shlib-destination
= makefile-pathname(t,
if (lib-desc & personal-library?(lib-desc))
makefile-variable-ref(t, "OPEN_DYLAN_USER_BIN")
else
makefile-variable-ref(t, "OPEN_DYLAN_RELEASE_BIN")
end if,
library-unit-name(t, target));
let xsym-source
= makefile-pathname(t, concatenate(library-unit-name(t, target), ".xSYM"));
let xsym-destination
= makefile-pathname(t,
if (lib-desc & personal-library?(lib-desc))
makefile-variable-ref(t, "OPEN_DYLAN_USER_BIN")
else
makefile-variable-ref(t, "OPEN_DYLAN_RELEASE_BIN")
end if,
concatenate(library-unit-name(t, target), ".xSYM"));
emit-makefile-rule
(t, stream, installed-libfile,
map(curry(makefile-variable-ref, t), #("LIBFILE")),
makefile-command(t, "{UNINSTALL} %s", stub-destination),
makefile-command(t, "{INSTALLLIB} %s %s", stub-source, stub-destination),
makefile-command(t, "{UNINSTALL} %s", shlib-destination),
makefile-command(t, "{INSTALLLIB} %s %s", shlib-source, shlib-destination),
makefile-command(t, "{UNINSTALL} %s", xsym-destination),
makefile-command(t, "{INSTALLLIB} %s %s", xsym-source, xsym-destination))
end method emit-lib-install-commands;
///---*** NOTE: This generic and its methods should probably be moved to the generic backend.
define generic emit-app-build-commands (t :: <makefile-target>, stream, exe);
define method emit-app-build-commands (t :: <makefile-target>, stream, exe)
emit-makefile-rule
(t, stream, exe,
map(curry(makefile-variable-ref, t), #("LIBFILE", "MAIN", "RC_OBJS")),
makefile-command(t,
"{LINKAPP}%s%s %s {MAIN} {RC_OBJS} {LIBFILE} {LIBS}"
" {C_LIBS} %s {APPRTLIBS} {LINKER_OPTIONS}",
link-target-separator(t), exe,
makefile-pathname(t, makefile-variable-ref(t, "GCDATASTART")),
makefile-pathname(t, makefile-variable-ref(t, "GCDATAEND"))))
end method emit-app-build-commands;
define method emit-app-build-commands (t :: <MPW-makefile-target>, stream, exe)
emit-makefile-rule
(t, stream, exe,
map(curry(makefile-variable-ref, t),
#("OBJS", "C_OBJS", "C_SRC_OBJS", "MAIN", "RC_OBJS")),
makefile-command(t,
"{LINKAPP}%s%s {MAIN} {RC_OBJS} {OBJS} {C_OBJS} {C_SRC_OBJS}"
" {LIBS} {C_LIBS} {APPRTLIBS} {LINKER_OPTIONS}",
link-target-separator(t), exe))
end method emit-app-build-commands;
///---*** NOTE: This generic and its methods should probably be moved to the generic backend.
define generic emit-app-install-commands (t :: <makefile-target>, stream, target);
define method emit-app-install-commands (t :: <makefile-target>, stream, target)
emit-makefile-rule
(t, stream, "install-app", #("app"),
makefile-command(t, "{UNINSTALL} %s", executable-unit-ref(t, target)),
makefile-command(t,
"{INSTALLLIB} %s %s",
makefile-pathname(t, makefile-variable-ref(t, "APP")),
makefile-pathname
(t, makefile-variable-ref(t, "OPEN_DYLAN_USER_BIN"))))
end method emit-app-install-commands;
define method emit-app-install-commands (t :: <MPW-makefile-target>, stream, target)
let xsym-source
= makefile-pathname(t, concatenate(executable-unit(t, target), ".xSYM"));
let xsym-destination
= makefile-pathname(t,
makefile-variable-ref(t, "OPEN_DYLAN_USER_BIN"),
concatenate(executable-unit(t, target), ".xSYM"));
emit-makefile-rule
(t, stream, "install-app", #("app"),
makefile-command(t, "{UNINSTALL} %s", executable-unit-ref(t, target)),
makefile-command(t,
"{INSTALLLIB} %s %s",
makefile-pathname(t, makefile-variable-ref(t, "APP")),
makefile-pathname
(t, makefile-variable-ref(t, "OPEN_DYLAN_USER_BIN"))),
makefile-command(t, "{UNINSTALL} %s", xsym-destination),
makefile-command(t, "{INSTALLLIB} %s %s", xsym-source, xsym-destination))
end method emit-app-install-commands;
define sideways method emit-target-makefile (back-end :: <c-back-end>,
t :: <makefile-target>,
makefile-name, lib-desc, units,
#key executable = #f,
base-address = #f,
linker-options = #(),
c-source-files = #(),
c-header-files = #(),
c-object-files = #(),
rc-files = #(),
c-libraries = #())
with-build-area-output (stream = lib-desc, name: makefile-name)
let name = library-description-emit-name(lib-desc);
let c-objects = map(curry(as,<string>), c-object-files);
let c-headers = map(curry(as,<string>), c-header-files);
let c-sources = map(curry(as,<string>), c-source-files);
// By the time we get here, c-sources is just the filename ...
let c-source-objects = map(curry(backend-object-file-name, t), c-sources);
// RC (resource) files apply only to Win32 targets ...
let rc-sources
= if (instance?(t, <win32-makefile-target>))
map(curry(as,<string>), rc-files)
else
#()
end;
// By the time we get here, rc-sources is just the filename ...
let rc-source-objects = map(curry(backend-resource-file-name, t), rc-sources);
let target = as-lowercase(as(<string>, name));
let target-filename = executable | target;
let glue = glue-unit(target);
let main = main-unit(target);
let all-units = add(units, glue);
let sources = source-units(t, all-units);
let objects = object-units(t, all-units);
let objects-names = object-units-names(t, all-units);
let lib = library-unit-name(t, target-filename);
let exe = executable-unit(t, target-filename);
emit-makefile-header(stream);
emit-makefile-program-setups(t, stream);
emit-makefile-separator(stream);
emit-makefile-definition(stream, "OBJS", objects);
emit-makefile-definition(stream, "SRCS", sources);
emit-makefile-definition(stream, "LINKER_OPTIONS", linker-options);
emit-makefile-definition(stream, "C_HEADERS", c-headers);
emit-makefile-definition(stream, "C_OBJS", c-objects);
emit-makefile-definition(stream, "C_SRC_OBJS", c-source-objects);
emit-makefile-definition(stream, "RC_OBJS", rc-source-objects);
emit-makefile-definition(stream, "C_LIBS", c-libraries);
// Somewhat bogus - we shouldn't be making assumptions about libraries used
// by our used libraries. Should just handle the directly used libraries,
// and let those fend for themselves. Unfortunately, this doesn't work
// for static linking. (It also doesn't work with shared libraries as
// symbols aren't re-exported.)
local method library-filename (ld) => (name :: <string>)
apply(ldbs-executable, library-description-build-settings(ld))
| library-string(ld)
end method;
let used-libraries = all-used-library-descriptions(lib-desc);
let used-library-names = map(library-string, used-libraries);
let used-library-filenames = map(library-filename, used-libraries);
let directly-used-libraries = directly-used-library-descriptions(lib-desc);
let directly-used-library-names
= map(library-string, directly-used-libraries);
let directly-used-library-filenames = map(library-filename, directly-used-libraries);
emit-makefile-definition
(stream, "LIBS",
map(curry(library-unit-link-ref, t), used-libraries, used-library-filenames));
let runtime-libraries
= concatenate(map(curry(runtime-unit-link-ref, t), #("run-time", "gc")),
additional-platform-libraries(t, #f),
derived-platform-libraries(t, c-libraries));
emit-makefile-definition(stream, "LIBRTLIBS", runtime-libraries);
let runtime-libraries
= concatenate(map(curry(runtime-unit-link-ref, t), #("run-time", "gc")),
additional-platform-libraries(t, #t),
derived-platform-libraries(t, c-libraries));
emit-makefile-definition(stream, "APPRTLIBS", runtime-libraries);
let main-objects = list(object-unit(t, main));
emit-makefile-definition(stream, "MAIN", main-objects);
emit-makefile-definition(stream, "LIBNAME", list(target-filename));
// Note: The following name can't be "LIB" because it would shadow a
// key environment variable under Windows.
emit-makefile-definition(stream, "LIBFILE", list(lib));
emit-makefile-definition(stream, "APP", list(exe));
emit-makefile-separator(stream);
emit-lib-build-commands(t, stream, lib);
emit-makefile-separator(stream);
emit-app-build-commands(t, stream, exe);
emit-makefile-separator(stream);
emit-makefile-rule
(t, stream, "lib", list(lib));
emit-makefile-separator(stream);
for (ld in directly-used-libraries,
lib-name in directly-used-library-names,
lib-filename in directly-used-library-filenames)
if (personal-library?(ld))
apply(emit-makefile-rule, t, stream, lib-filename,
list(library-unit-ref(t, ld, lib-filename)), #[]);
emit-makefile-separator(stream);
apply(emit-makefile-rule,
t, stream, library-unit-ref(t, ld, lib-filename),
list(relative-makefile-unit-ref(t, lib-name)),
vector(make-install-command(t, lib-name)))
else
apply(emit-makefile-rule, t, stream, lib-name, #(), #[]);
end if;
emit-makefile-separator(stream);
end for;
// NOTE -- This creates inconsistent Makefiles if the library is a system library.
// In particular, these rules build into PERSONAL_LIB but delete from SYSTEM_LIB.
let installed-libfile
= makefile-pathname(t,
makefile-variable-ref(t, "OPEN_DYLAN_USER_LIB"),
makefile-variable-ref(t, "LIBFILE"));
emit-lib-install-commands(t, stream, installed-libfile, lib-desc, target);
emit-makefile-separator(stream);
emit-makefile-rule
(t, stream, "install",
concatenate(directly-used-library-filenames,
list(installed-libfile)));
emit-makefile-separator(stream);
emit-makefile-rule
(t, stream, "app", list("install", exe));
emit-makefile-separator(stream);
emit-app-install-commands(t, stream, target);
emit-makefile-separator(stream);
emit-makefile-rule
(t, stream, "clean", #(),
makefile-command(t,
"{UNINSTALL} {OBJS} {LIBFILE} {APP} {C_SRC_OBJS} {RC_OBJS} %s",
object-unit(t, main)));
emit-makefile-separator(stream);
emit-makefile-rule
(t, stream, "clean-all", #("clean"),
makefile-command(t, "{UNINSTALL} {SRCS} %s", source-unit(t, main)));
emit-makefile-separator(stream);
emit-makefile-rule
(t, stream, "run", #("app"), exe);
emit-makefile-separator(stream);
emit-makefile-object-dependencies
(back-end, t, stream,
concatenate(vector(source-unit(t, main)), sources, c-sources, rc-sources),
concatenate(vector(object-unit(t, main)), objects, c-source-objects,
rc-source-objects),
concatenate(vector(object-unit-name(t, main)), objects-names,
c-source-objects, rc-source-objects),
concatenate(make(<vector>, size: size(sources) + size(c-sources) + 1,
fill: makefile-command(t, "{CC} {CFLAGS}")),
make(<vector>, size: size(rc-sources),
fill: makefile-command(t, "{RC} {RFLAGS}"))),
concatenate(vector(#()), make(<vector>, size: size(sources), fill: #()),
make(<vector>, size: size(c-sources),
fill: list(makefile-variable-ref(t, "C_HEADERS"))),
make(<vector>, size: size(rc-sources),
fill: list(makefile-variable-ref(t, "C_HEADERS")))));
emit-makefile-footer(stream);
emit-build-system-files(target,
lib-desc,
objects,
main-objects,
map(curry(library-unit-link-ref, t),
used-libraries, used-library-filenames),
runtime-libraries);
end with-build-area-output;
end method emit-target-makefile;
define method emit-build-system-files
(name, ld, objects, main-objects, libraries, runtime-libraries)
with-build-area-output (link-stream = ld, name: concatenate(name, ".linkexe"))
for (file-unit in objects)
format(link-stream, "%s\n", file-unit);
end for;
for (file-unit in main-objects)
format(link-stream, "%s\n", file-unit);
end for;
for (lib in libraries.reverse)
format(link-stream, "%s\n", lib);
end for;
for (lib in runtime-libraries)
format(link-stream, "%s\n", lib);
end for;
end with-build-area-output;
with-build-area-output (link-stream = ld, name: concatenate(name, ".linklib"))
for (file-unit in objects)
format(link-stream, "%s\n", file-unit);
end for;
end with-build-area-output;
end method emit-build-system-files;
syntax highlighted by Code2HTML, v. 0.9.1