diff --git a/Makefile.in b/Makefile.in index f5bb3cb2ed0..73400610f67 100644 --- a/Makefile.in +++ b/Makefile.in @@ -130,6 +130,14 @@ ifndef DEBUG_BORROWS RUSTFLAGS_STAGE2 += -Z no-debug-borrows endif +# The executables crated during this compilation process have no need to include +# static copies of libstd and libextra. We also generate dynamic versions of all +# libraries, so in the interest of space, prefer dynamic linking throughout the +# compilation process. +RUSTFLAGS_STAGE1 += -Z prefer-dynamic +RUSTFLAGS_STAGE2 += -Z prefer-dynamic +RUSTFLAGS_STAGE3 += -Z prefer-dynamic + # platform-specific auto-configuration include $(CFG_SRC_DIR)mk/platform.mk @@ -239,6 +247,10 @@ LIBRUSTPKG_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustpkg) LIBRUSTDOC_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustdoc) LIBRUSTUV_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustuv) +EXTRALIB_RGLOB_$(1) :=$(call CFG_RLIB_GLOB,extra) +STDLIB_RGLOB_$(1) :=$(call CFG_RLIB_GLOB,std) +LIBRUSTUV_RGLOB_$(1) :=$(call CFG_RLIB_GLOB,rustuv) + endef # $(1) is the path for directory to match against @@ -392,42 +404,25 @@ TBIN$(1)_T_$(2)_H_$(3) = $$(TROOT$(1)_T_$(2)_H_$(3))/bin TLIB$(1)_T_$(2)_H_$(3) = $$(TROOT$(1)_T_$(2)_H_$(3))/$$(CFG_LIBDIR) # The name of the standard and extra libraries used by rustc -ifdef CFG_DISABLE_SHAREDSTD - HSTDLIB_DEFAULT$(1)_H_$(3) = \ - $$(HLIB$(1)_H_$(3))/libstd.rlib - TSTDLIB_DEFAULT$(1)_T_$(2)_H_$(3) = \ - $$(TLIB$(1)_T_$(2)_H_$(3))/libstd.rlib +HSTDLIB_DEFAULT$(1)_H_$(3) = \ + $$(HLIB$(1)_H_$(3))/$(CFG_STDLIB_$(3)) +TSTDLIB_DEFAULT$(1)_T_$(2)_H_$(3) = \ + $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_STDLIB_$(2)) - HEXTRALIB_DEFAULT$(1)_H_$(3) = \ - $$(HLIB$(1)_H_$(3))/libextra.rlib - TEXTRALIB_DEFAULT$(1)_T_$(2)_H_$(3) = \ - $$(TLIB$(1)_T_$(2)_H_$(3))/libextra.rlib +HEXTRALIB_DEFAULT$(1)_H_$(3) = \ + $$(HLIB$(1)_H_$(3))/$(CFG_EXTRALIB_$(3)) +TEXTRALIB_DEFAULT$(1)_T_$(2)_H_$(3) = \ + $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_EXTRALIB_$(2)) - HLIBRUSTC_DEFAULT$(1)_H_$(3) = \ - $$(HLIB$(1)_H_$(3))/librustc.rlib - TLIBRUSTC_DEFAULT$(1)_T_$(2)_H_$(3) = \ - $$(TLIB$(1)_T_$(2)_H_$(3))/librustc.rlib -else - HSTDLIB_DEFAULT$(1)_H_$(3) = \ - $$(HLIB$(1)_H_$(3))/$(CFG_STDLIB_$(3)) - TSTDLIB_DEFAULT$(1)_T_$(2)_H_$(3) = \ - $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_STDLIB_$(2)) +HLIBRUSTC_DEFAULT$(1)_H_$(3) = \ + $$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTC_$(3)) +TLIBRUSTC_DEFAULT$(1)_T_$(2)_H_$(3) = \ + $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(2)) - HEXTRALIB_DEFAULT$(1)_H_$(3) = \ - $$(HLIB$(1)_H_$(3))/$(CFG_EXTRALIB_$(3)) - TEXTRALIB_DEFAULT$(1)_T_$(2)_H_$(3) = \ - $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_EXTRALIB_$(2)) - - HLIBRUSTC_DEFAULT$(1)_H_$(3) = \ - $$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTC_$(3)) - TLIBRUSTC_DEFAULT$(1)_T_$(2)_H_$(3) = \ - $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(2)) - - HLIBRUSTUV_DEFAULT$(1)_H_$(3) = \ - $$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTUV_$(3)) - TLIBRUSTUV_DEFAULT$(1)_T_$(2)_H_$(3) = \ - $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTUV_$(2)) -endif +HLIBRUSTUV_DEFAULT$(1)_H_$(3) = \ + $$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTUV_$(3)) +TLIBRUSTUV_DEFAULT$(1)_T_$(2)_H_$(3) = \ + $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTUV_$(2)) # Preqrequisites for using the stageN compiler HSREQ$(1)_H_$(3) = \ diff --git a/configure b/configure index 2390016b3f1..074e522a20c 100755 --- a/configure +++ b/configure @@ -364,7 +364,6 @@ fi BOOL_OPTIONS="" VAL_OPTIONS="" -opt sharedstd 1 "build libstd as a shared library" opt valgrind 0 "run tests with valgrind (memcheck by default)" opt helgrind 0 "run tests with helgrind instead of memcheck" opt docs 1 "build documentation" @@ -398,7 +397,7 @@ valopt sysconfdir "/etc" "install system configuration files" valopt datadir "${CFG_PREFIX}/share" "install data" valopt infodir "${CFG_PREFIX}/share/info" "install additional info" valopt mandir "${CFG_PREFIX}/share/man" "install man pages in PATH" -valopt libdir "${CFG_PREFIX}/lib" "install libraries" +valopt libdir "${CFG_PREFIX}/lib" "install libraries" # Validate Options step_msg "validating $CFG_SELF args" diff --git a/doc/rust.md b/doc/rust.md index a25f19371bd..969e40e632a 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -1507,19 +1507,15 @@ an `abi` string, as shown here: extern "stdcall" { } ~~~~ -The `link_name` attribute allows the name of the library to be specified. +The `link` attribute allows the name of the library to be specified. When +specified the compiler will attempt to link against the native library of the +specified name. ~~~~ {.xfail-test} -#[link_name = "crypto"] +#[link(name = "crypto")] extern { } ~~~~ -The `nolink` attribute tells the Rust compiler -not to do any linking for the external block. -This is particularly useful for creating external blocks for libc, -which tends to not follow standard library naming conventions -and is linked to all Rust programs anyway. - The type of a function declared in an extern block is `extern "abi" fn(A1, ..., An) -> R`, diff --git a/mk/clean.mk b/mk/clean.mk index dc2aefeb865..1994fec0990 100644 --- a/mk/clean.mk +++ b/mk/clean.mk @@ -59,6 +59,7 @@ clean-generic-$(2)-$(1): $(Q)find $(1)/rustllvm \ $(1)/rt \ $(1)/test \ + $(1)/stage* \ -name '*.[odasS]' -o \ -name '*.so' -o \ -name '*.dylib' -o \ @@ -91,13 +92,16 @@ clean$(1)_H_$(2): $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTC_$(2)) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBSYNTAX_$(2)) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(STDLIB_GLOB_$(2)) + $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(STDLIB_RGLOB_$(2)) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(EXTRALIB_GLOB_$(2)) + $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(EXTRALIB_RGLOB_$(2)) + $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTUV_GLOB_$(2)) + $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTUV_RGLOB_$(2)) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTC_GLOB_$(2)) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBSYNTAX_GLOB_$(2)) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTPKG_GLOB_$(2)) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTDOC_GLOB_$(2)) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_RUSTLLVM_$(2)) - $(Q)rm -f $$(HLIB$(1)_H_$(2))/libstd.rlib endef @@ -122,14 +126,16 @@ clean$(1)_T_$(2)_H_$(3): $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(2)) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBSYNTAX_$(2)) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(STDLIB_GLOB_$(2)) + $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(STDLIB_RGLOB_$(2)) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(EXTRALIB_GLOB_$(2)) + $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(EXTRALIB_RGLOB_$(2)) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTUV_GLOB_$(2)) + $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTUV_RGLOB_$(2)) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTC_GLOB_$(2)) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBSYNTAX_GLOB_$(2)) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTPKG_GLOB_$(2)) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTDOC_GLOB_$(2)) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUSTLLVM_$(2)) - $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/libstd.rlib $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/libmorestack.a $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/librun_pass_stage* # For unix $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/run_pass_stage* # For windows diff --git a/mk/host.mk b/mk/host.mk index 9ba2b978f10..7e8a3e8a6eb 100644 --- a/mk/host.mk +++ b/mk/host.mk @@ -50,7 +50,7 @@ $$(HLIB$(2)_H_$(4))/$(CFG_LIBRUSTC_$(4)): \ $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTC_GLOB_$(4)),$$(notdir $$@)) $$(Q)cp $$< $$@ $$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTC_GLOB_$(4)) \ - $(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTC_DSYM_GLOB_$(4))) \ + $$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTC_DSYM_GLOB_$(4))) \ $$(HLIB$(2)_H_$(4)) $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTC_GLOB_$(4)),$$(notdir $$@)) @@ -82,6 +82,7 @@ $$(HLIB$(2)_H_$(4))/$(CFG_STDLIB_$(4)): \ | $$(HLIB$(2)_H_$(4))/ @$$(call E, cp: $$@) $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_GLOB_$(4)),$$(notdir $$@)) + $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_RGLOB_$(4)),$$(notdir $$@)) $$(Q)cp $$< $$@ # Subtle: We do not let the shell expand $$(STDLIB_DSYM_GLOB) directly rather # we use Make's $$(wildcard) facility. The reason is that, on mac, when using @@ -91,9 +92,11 @@ $$(HLIB$(2)_H_$(4))/$(CFG_STDLIB_$(4)): \ # Make instead expands the glob to nothing, which gives us the correct behavior. # (Copy .dsym file if it exists, but do nothing otherwise) $$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(STDLIB_GLOB_$(4)) \ + $$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(STDLIB_RGLOB_$(4))) \ $$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(STDLIB_DSYM_GLOB_$(4))) \ $$(HLIB$(2)_H_$(4)) $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_GLOB_$(4)),$$(notdir $$@)) + $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_RGLOB_$(4)),$$(notdir $$@)) $$(HLIB$(2)_H_$(4))/$(CFG_EXTRALIB_$(4)): \ $$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_EXTRALIB_$(4)) \ @@ -102,11 +105,14 @@ $$(HLIB$(2)_H_$(4))/$(CFG_EXTRALIB_$(4)): \ | $$(HLIB$(2)_H_$(4))/ @$$(call E, cp: $$@) $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_GLOB_$(4)),$$(notdir $$@)) + $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_RGLOB_$(4)),$$(notdir $$@)) $$(Q)cp $$< $$@ $$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(EXTRALIB_GLOB_$(4)) \ + $$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(EXTRALIB_RGLOB_$(4))) \ $$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(EXTRALIB_DSYM_GLOB_$(4))) \ $$(HLIB$(2)_H_$(4)) $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_GLOB_$(4)),$$(notdir $$@)) + $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_RGLOB_$(4)),$$(notdir $$@)) $$(HLIB$(2)_H_$(4))/$(CFG_LIBRUSTUV_$(4)): \ $$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTUV_$(4)) \ @@ -115,35 +121,14 @@ $$(HLIB$(2)_H_$(4))/$(CFG_LIBRUSTUV_$(4)): \ | $$(HLIB$(2)_H_$(4))/ @$$(call E, cp: $$@) $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_GLOB_$(4)),$$(notdir $$@)) + $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_RGLOB_$(4)),$$(notdir $$@)) $$(Q)cp $$< $$@ $$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTUV_GLOB_$(4)) \ + $$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTUV_RGLOB_$(4))) \ $$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTUV_DSYM_GLOB_$(4))) \ $$(HLIB$(2)_H_$(4)) $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_GLOB_$(4)),$$(notdir $$@)) - -$$(HLIB$(2)_H_$(4))/libstd.rlib: \ - $$(TLIB$(1)_T_$(4)_H_$(3))/libstd.rlib \ - $$(HLIB$(2)_H_$(4))/$$(CFG_RUNTIME_$(4)) \ - | $$(HLIB$(2)_H_$(4))/ - @$$(call E, cp: $$@) - $$(Q)cp $$< $$@ - -$$(HLIB$(2)_H_$(4))/libextra.rlib: \ - $$(TLIB$(1)_T_$(4)_H_$(3))/libextra.rlib \ - $$(HLIB$(2)_H_$(4))/libstd.rlib \ - $$(HLIB$(2)_H_$(4))/$$(CFG_RUNTIME_$(4)) \ - | $$(HLIB$(2)_H_$(4))/ - @$$(call E, cp: $$@) - $$(Q)cp $$< $$@ - -$$(HLIB$(2)_H_$(4))/librustc.rlib: \ - $$(TLIB$(1)_T_$(4)_H_$(3))/librustc.rlib \ - $$(HLIB$(2)_H_$(4))/libstd.rlib \ - $$(HLIB$(2)_H_$(4))/libextra.rlib \ - $$(HLIB$(2)_H_$(4))/$$(CFG_RUNTIME_$(4)) \ - | $$(HLIB$(2)_H_$(4))/ - @$$(call E, cp: $$@) - $$(Q)cp $$< $$@ + $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_RGLOB_$(4)),$$(notdir $$@)) $$(HLIB$(2)_H_$(4))/$(CFG_RUSTLLVM_$(4)): \ $$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_RUSTLLVM_$(4)) \ diff --git a/mk/install.mk b/mk/install.mk index 49f1fdbf547..d8208003b7b 100644 --- a/mk/install.mk +++ b/mk/install.mk @@ -144,8 +144,11 @@ install-host: $(CSREQ$(ISTAGE)_T_$(CFG_BUILD_)_H_$(CFG_BUILD_)) $(Q)$(call INSTALL,$(HB2),$(PHB),rustpkg$(X_$(CFG_BUILD))) $(Q)$(call INSTALL,$(HB2),$(PHB),rustdoc$(X_$(CFG_BUILD))) $(Q)$(call INSTALL_LIB,$(STDLIB_GLOB_$(CFG_BUILD))) + $(Q)$(call INSTALL_LIB,$(STDLIB_RGLOB_$(CFG_BUILD))) $(Q)$(call INSTALL_LIB,$(EXTRALIB_GLOB_$(CFG_BUILD))) + $(Q)$(call INSTALL_LIB,$(EXTRALIB_RGLOB_$(CFG_BUILD))) $(Q)$(call INSTALL_LIB,$(LIBRUSTUV_GLOB_$(CFG_BUILD))) + $(Q)$(call INSTALL_LIB,$(LIBRUSTUV_RGLOB_$(CFG_BUILD))) $(Q)$(call INSTALL_LIB,$(LIBRUSTC_GLOB_$(CFG_BUILD))) $(Q)$(call INSTALL_LIB,$(LIBSYNTAX_GLOB_$(CFG_BUILD))) $(Q)$(call INSTALL_LIB,$(LIBRUSTPKG_GLOB_$(CFG_BUILD))) @@ -170,8 +173,11 @@ uninstall: $(Q)rm -f $(PHL)/$(CFG_RUNTIME_$(CFG_BUILD)) $(Q)for i in \ $(call HOST_LIB_FROM_HL_GLOB,$(STDLIB_GLOB_$(CFG_BUILD))) \ + $(call HOST_LIB_FROM_HL_GLOB,$(STDLIB_RGLOB_$(CFG_BUILD))) \ $(call HOST_LIB_FROM_HL_GLOB,$(EXTRALIB_GLOB_$(CFG_BUILD))) \ + $(call HOST_LIB_FROM_HL_GLOB,$(EXTRALIB_RGLOB_$(CFG_BUILD))) \ $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTUV_GLOB_$(CFG_BUILD))) \ + $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTUV_RGLOB_$(CFG_BUILD))) \ $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTC_GLOB_$(CFG_BUILD))) \ $(call HOST_LIB_FROM_HL_GLOB,$(LIBSYNTAX_GLOB_$(CFG_BUILD))) \ $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTPKG_GLOB_$(CFG_BUILD))) \ diff --git a/mk/platform.mk b/mk/platform.mk index bc2536cce48..35d4279eaef 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -138,6 +138,7 @@ endif endif endif +CFG_RLIB_GLOB=lib$(1)-*.rlib # x86_64-unknown-linux-gnu configuration CC_x86_64-unknown-linux-gnu=$(CC) diff --git a/mk/target.mk b/mk/target.mk index f7d8ec83a5a..a8606cbdbcb 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -60,8 +60,10 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_STDLIB_$(2)): \ | $$(TLIB$(1)_T_$(2)_H_$(3))/ @$$(call E, compile_and_link: $$@) $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_GLOB_$(2)),$$(notdir $$@)) + $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_RGLOB_$(2)),$$(notdir $$@)) $$(STAGE$(1)_T_$(2)_H_$(3)) $$(WFLAGS_ST$(1)) --out-dir $$(@D) $$< && touch $$@ $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_GLOB_$(2)),$$(notdir $$@)) + $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(STDLIB_RGLOB_$(2)),$$(notdir $$@)) $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_EXTRALIB_$(2)): \ $$(EXTRALIB_CRATE) $$(EXTRALIB_INPUTS) \ @@ -70,8 +72,10 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_EXTRALIB_$(2)): \ | $$(TLIB$(1)_T_$(2)_H_$(3))/ @$$(call E, compile_and_link: $$@) $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_GLOB_$(2)),$$(notdir $$@)) + $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_RGLOB_$(2)),$$(notdir $$@)) $$(STAGE$(1)_T_$(2)_H_$(3)) $$(WFLAGS_ST$(1)) --out-dir $$(@D) $$< && touch $$@ $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_GLOB_$(2)),$$(notdir $$@)) + $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(EXTRALIB_RGLOB_$(2)),$$(notdir $$@)) $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTUV_$(2)): \ $$(LIBRUSTUV_CRATE) $$(LIBRUSTUV_INPUTS) \ @@ -82,11 +86,13 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTUV_$(2)): \ | $$(TLIB$(1)_T_$(2)_H_$(3))/ @$$(call E, compile_and_link: $$@) $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_GLOB_$(2)),$$(notdir $$@)) + $$(call REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_RGLOB_$(2)),$$(notdir $$@)) $$(STAGE$(1)_T_$(2)_H_$(3)) $$(WFLAGS_ST$(1)) \ -L $$(UV_SUPPORT_DIR_$(2)) \ -L $$(dir $$(LIBUV_LIB_$(2))) \ --out-dir $$(@D) $$< && touch $$@ $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_GLOB_$(2)),$$(notdir $$@)) + $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTUV_RGLOB_$(2)),$$(notdir $$@)) $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBSYNTAX_$(3)): \ $$(LIBSYNTAX_CRATE) $$(LIBSYNTAX_INPUTS) \ diff --git a/mk/tests.mk b/mk/tests.mk index a24791d76af..c3614cbcc6b 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -584,6 +584,10 @@ TEST_SREQ$(1)_T_$(2)_H_$(3) = \ # remove directive, if present, from CFG_RUSTC_FLAGS (issue #7898). CTEST_RUSTC_FLAGS := $$(subst --cfg ndebug,,$$(CFG_RUSTC_FLAGS)) +# There's no need our entire test suite to take up gigabytes of space on disk +# including copies of libstd/libextra all over the place +CTEST_RUSTC_FLAGS := $$(CTEST_RUSTC_FLAGS) -Z prefer-dynamic + # The tests can not be optimized while the rest of the compiler is optimized, so # filter out the optimization (if any) from rustc and then figure out if we need # to be optimized diff --git a/src/libextra/flate.rs b/src/libextra/flate.rs index 3d1d0c91e31..a4a10ccfa73 100644 --- a/src/libextra/flate.rs +++ b/src/libextra/flate.rs @@ -23,7 +23,7 @@ pub mod rustrt { use std::libc::{c_int, c_void, size_t}; - #[link_name = "rustrt"] + #[link(name = "rustrt")] extern { pub fn tdefl_compress_mem_to_heap(psrc_buf: *c_void, src_buf_len: size_t, diff --git a/src/libextra/lib.rs b/src/libextra/lib.rs index a74c4993be3..571891f1830 100644 --- a/src/libextra/lib.rs +++ b/src/libextra/lib.rs @@ -32,7 +32,9 @@ #[comment = "Rust extras"]; #[license = "MIT/ASL2"]; -#[crate_type = "lib"]; +#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot +#[crate_type = "rlib"]; +#[crate_type = "dylib"]; #[feature(macro_rules, globs, managed_boxes)]; diff --git a/src/librustc/back/archive.rs b/src/librustc/back/archive.rs new file mode 100644 index 00000000000..e3c8e859f72 --- /dev/null +++ b/src/librustc/back/archive.rs @@ -0,0 +1,128 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A helper class for dealing with static archives + +use driver::session::Session; +use metadata::filesearch; + +use std::io::fs; +use std::os; +use std::run::{ProcessOptions, Process, ProcessOutput}; +use std::str; +use extra::tempfile::TempDir; +use syntax::abi; + +pub struct Archive { + priv sess: Session, + priv dst: Path, +} + +fn run_ar(sess: Session, args: &str, cwd: Option<&Path>, + paths: &[&Path]) -> ProcessOutput { + let ar = sess.opts.ar.clone().unwrap_or(~"ar"); + let mut args = ~[args.to_owned()]; + let mut paths = paths.iter().map(|p| p.as_str().unwrap().to_owned()); + args.extend(&mut paths); + let mut opts = ProcessOptions::new(); + opts.dir = cwd; + debug!("{} {}", ar, args.connect(" ")); + match cwd { + Some(p) => { debug!("inside {}", p.display()); } + None => {} + } + let o = Process::new(ar, args.as_slice(), opts).finish_with_output(); + if !o.status.success() { + sess.err(format!("{} failed with: {}", ar, o.status)); + sess.note(format!("stdout ---\n{}", str::from_utf8(o.output))); + sess.note(format!("stderr ---\n{}", str::from_utf8(o.error))); + sess.abort_if_errors(); + } + o +} + +impl Archive { + /// Initializes a new static archive with the given object file + pub fn create<'a>(sess: Session, dst: &'a Path, + initial_object: &'a Path) -> Archive { + run_ar(sess, "crus", None, [dst, initial_object]); + Archive { sess: sess, dst: dst.clone() } + } + + /// Opens an existing static archive + pub fn open(sess: Session, dst: Path) -> Archive { + assert!(dst.exists()); + Archive { sess: sess, dst: dst } + } + + /// Read a file in the archive + pub fn read(&self, file: &str) -> ~[u8] { + run_ar(self.sess, "p", None, [&self.dst, &Path::new(file)]).output + } + + /// Adds all of the contents of a native library to this archive. This will + /// search in the relevant locations for a library named `name`. + pub fn add_native_library(&mut self, name: &str) { + let location = self.find_library(name); + self.add_archive(&location, name); + } + + /// Adds all of the contents of the rlib at the specified path to this + /// archive. + pub fn add_rlib(&mut self, rlib: &Path) { + let name = rlib.filename_str().unwrap().split_iter('-').next().unwrap(); + self.add_archive(rlib, name); + } + + fn add_archive(&mut self, archive: &Path, name: &str) { + let loc = TempDir::new("rsar").unwrap(); + + // First, extract the contents of the archive to a temporary directory + let archive = os::make_absolute(archive); + run_ar(self.sess, "x", Some(loc.path()), [&archive]); + + // Next, we must rename all of the inputs to "guaranteed unique names". + // The reason for this is that archives are keyed off the name of the + // files, so if two files have the same name they will override one + // another in the archive (bad). + let files = fs::readdir(loc.path()); + let mut inputs = ~[]; + for file in files.iter() { + let filename = file.filename_str().unwrap(); + let filename = format!("r-{}-{}", name, filename); + let new_filename = file.with_filename(filename); + fs::rename(file, &new_filename); + inputs.push(new_filename); + } + + // Finally, add all the renamed files to this archive + let mut args = ~[&self.dst]; + args.extend(&mut inputs.iter()); + run_ar(self.sess, "r", None, args.as_slice()); + } + + fn find_library(&self, name: &str) -> Path { + let (prefix, ext) = match self.sess.targ_cfg.os { + abi::OsWin32 => ("", "lib"), _ => ("lib", "a"), + }; + let libname = format!("{}{}.{}", prefix, name, ext); + + let mut rustpath = filesearch::rust_path(); + rustpath.push(self.sess.filesearch.get_target_lib_path()); + let path = self.sess.opts.addl_lib_search_paths.iter(); + for path in path.chain(rustpath.iter()) { + debug!("looking for {} inside {}", name, path.display()); + let test = path.join(libname.clone()); + if test.exists() { return test } + } + self.sess.fatal(format!("could not find native static library `{}`, \ + perhaps an -L flag is missing?", name)); + } +} diff --git a/src/librustc/back/arm.rs b/src/librustc/back/arm.rs index a3ac468a5f0..c155f4bd15b 100644 --- a/src/librustc/back/arm.rs +++ b/src/librustc/back/arm.rs @@ -63,6 +63,6 @@ pub fn get_target_strs(target_triple: ~str, target_os: abi::Os) -> target_strs:: target_triple: target_triple, - cc_args: ~[~"-marm"] + cc_args: ~[~"-marm"], }; } diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 9aba16422d3..98d5958ac4f 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -9,6 +9,7 @@ // except according to those terms. +use back::archive::Archive; use back::rpath; use driver::session::Session; use driver::session; @@ -16,7 +17,7 @@ use lib::llvm::ModuleRef; use lib; use metadata::common::LinkMeta; -use metadata::{encoder, cstore, filesearch}; +use metadata::{encoder, cstore, filesearch, csearch}; use middle::trans::context::CrateContext; use middle::trans::common::gensym_name; use middle::ty; @@ -30,7 +31,6 @@ use std::ptr; use std::run; use std::str; -use std::vec; use std::io::fs; use syntax::abi; use syntax::ast; @@ -88,7 +88,6 @@ pub mod jit { use driver::session::Session; use lib::llvm::llvm; use lib::llvm::{ModuleRef, ContextRef, ExecutionEngineRef}; - use metadata::cstore; use std::c_str::ToCStr; use std::cast; @@ -125,19 +124,6 @@ pub fn exec(sess: Session, // core linked into rustc. We don't want that, // incase the user wants to use an older extra library. - let cstore = sess.cstore; - let r = cstore::get_used_crate_files(cstore); - for cratepath in r.iter() { - debug!("linking: {}", cratepath.display()); - - cratepath.with_c_str(|buf_t| { - if !llvm::LLVMRustLoadCrate(manager, buf_t) { - llvm_err(sess, ~"Could not link"); - } - debug!("linked: {}", cratepath.display()); - }) - } - // We custom-build a JIT execution engine via some rust wrappers // first. This wrappers takes ownership of the module passed in. let ee = llvm::LLVMRustBuildJIT(manager, m, stacks); @@ -368,20 +354,20 @@ pub fn run_passes(sess: Session, } pub fn run_assembler(sess: Session, assembly: &Path, object: &Path) { - let cc_prog = super::get_cc_prog(sess); + let cc = super::get_cc_prog(sess); // FIXME (#9639): This needs to handle non-utf8 paths - let cc_args = ~[ + let args = [ ~"-c", ~"-o", object.as_str().unwrap().to_owned(), assembly.as_str().unwrap().to_owned()]; - let prog = run::process_output(cc_prog, cc_args); + debug!("{} {}", cc, args.connect(" ")); + let prog = run::process_output(cc, args); if !prog.status.success() { - sess.err(format!("linking with `{}` failed: {}", cc_prog, prog.status)); - sess.note(format!("{} arguments: {}", - cc_prog, cc_args.connect(" "))); + sess.err(format!("linking with `{}` failed: {}", cc, prog.status)); + sess.note(format!("{} arguments: {}", cc, args.connect(" "))); sess.note(str::from_utf8(prog.error + prog.output)); sess.abort_if_errors(); } @@ -876,90 +862,71 @@ pub fn mangle_internal_name_by_path(ccx: &mut CrateContext, path: path) -> ~str mangle(ccx.sess, path, None, None) } - -pub fn output_dll_filename(os: abi::Os, lm: LinkMeta) -> ~str { - let (dll_prefix, dll_suffix) = match os { - abi::OsWin32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX), - abi::OsMacos => (macos::DLL_PREFIX, macos::DLL_SUFFIX), - abi::OsLinux => (linux::DLL_PREFIX, linux::DLL_SUFFIX), - abi::OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX), - abi::OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX), - }; - format!("{}{}-{}-{}{}", dll_prefix, lm.name, lm.extras_hash, lm.vers, dll_suffix) +pub fn output_lib_filename(lm: LinkMeta) -> ~str { + format!("{}-{}-{}", lm.name, lm.extras_hash, lm.vers) } pub fn get_cc_prog(sess: Session) -> ~str { + match sess.opts.linker { + Some(ref linker) => return linker.to_owned(), + None => {} + } + // In the future, FreeBSD will use clang as default compiler. // It would be flexible to use cc (system's default C compiler) // instead of hard-coded gcc. - // For win32, there is no cc command, so we add a condition to make it use g++. - // We use g++ rather than gcc because it automatically adds linker options required - // for generation of dll modules that correctly register stack unwind tables. - match sess.opts.linker { - Some(ref linker) => linker.to_str(), - None => match sess.targ_cfg.os { - abi::OsAndroid => - match &sess.opts.android_cross_path { - &Some(ref path) => { - format!("{}/bin/arm-linux-androideabi-gcc", *path) - } - &None => { - sess.fatal("need Android NDK path for linking \ - (--android-cross-path)") - } - }, - abi::OsWin32 => ~"g++", - _ => ~"cc" - } + // For win32, there is no cc command, so we add a condition to make it use + // g++. We use g++ rather than gcc because it automatically adds linker + // options required for generation of dll modules that correctly register + // stack unwind tables. + match sess.targ_cfg.os { + abi::OsAndroid => match sess.opts.android_cross_path { + Some(ref path) => format!("{}/bin/arm-linux-androideabi-gcc", *path), + None => { + sess.fatal("need Android NDK path for linking \ + (--android-cross-path)") + } + }, + abi::OsWin32 => ~"g++", + _ => ~"cc", } } -// If the user wants an exe generated we need to invoke -// cc to link the object file with some libs +/// Perform the linkage portion of the compilation phase. This will generate all +/// of the requested outputs for this compilation session. pub fn link_binary(sess: Session, + crate_types: &[~str], obj_filename: &Path, out_filename: &Path, lm: LinkMeta) { - - let cc_prog = get_cc_prog(sess); - // The invocations of cc share some flags across platforms - - let output = if *sess.building_library { - let long_libname = output_dll_filename(sess.targ_cfg.os, lm); - debug!("link_meta.name: {}", lm.name); - debug!("long_libname: {}", long_libname); - debug!("out_filename: {}", out_filename.display()); - let out_dirname = out_filename.dir_path(); - debug!("dirname(out_filename): {}", out_dirname.display()); - - out_filename.with_filename(long_libname) + let outputs = if sess.opts.test { + // If we're generating a test executable, then ignore all other output + // styles at all other locations + ~[session::OutputExecutable] } else { - out_filename.clone() + // Always generate whatever was specified on the command line, but also + // look at what was in the crate file itself for generating output + // formats. + let mut outputs = sess.opts.outputs.clone(); + for ty in crate_types.iter() { + if "bin" == *ty { + outputs.push(session::OutputExecutable); + } else if "dylib" == *ty || "lib" == *ty { + outputs.push(session::OutputDylib); + } else if "rlib" == *ty { + outputs.push(session::OutputRlib); + } else if "staticlib" == *ty { + outputs.push(session::OutputStaticlib); + } + } + if outputs.len() == 0 { + outputs.push(session::OutputExecutable); + } + outputs }; - debug!("output: {}", output.display()); - let cc_args = link_args(sess, obj_filename, out_filename, lm); - debug!("{} link args: {}", cc_prog, cc_args.connect(" ")); - if (sess.opts.debugging_opts & session::print_link_args) != 0 { - println!("{} link args: {}", cc_prog, cc_args.connect(" ")); - } - - // We run 'cc' here - let prog = run::process_output(cc_prog, cc_args); - - if !prog.status.success() { - sess.err(format!("linking with `{}` failed: {}", cc_prog, prog.status)); - sess.note(format!("{} arguments: {}", - cc_prog, cc_args.connect(" "))); - sess.note(str::from_utf8(prog.error + prog.output)); - sess.abort_if_errors(); - } - - // On OSX, debuggers needs this utility to get run to do some munging of the - // symbols - if sess.targ_cfg.os == abi::OsMacos && sess.opts.debuginfo { - // FIXME (#9639): This needs to handle non-utf8 paths - run::process_status("dsymutil", [output.as_str().unwrap().to_owned()]); + for output in outputs.move_iter() { + link_binary_output(sess, output, obj_filename, out_filename, lm); } // Remove the temporary object file if we aren't saving temps @@ -971,34 +938,36 @@ pub fn link_binary(sess: Session, fn is_writeable(p: &Path) -> bool { use std::io; - !p.exists() || - (match io::result(|| p.stat()) { - Err(..) => false, - Ok(m) => m.perm & io::UserWrite == io::UserWrite - }) + match io::result(|| p.stat()) { + Err(..) => true, + Ok(m) => m.perm & io::UserWrite == io::UserWrite + } } -pub fn link_args(sess: Session, - obj_filename: &Path, - out_filename: &Path, - lm:LinkMeta) -> ~[~str] { - - // Converts a library file-stem into a cc -l argument - fn unlib(config: @session::config, stem: ~str) -> ~str { - if stem.starts_with("lib") && - config.os != abi::OsWin32 { - stem.slice(3, stem.len()).to_owned() - } else { - stem +fn link_binary_output(sess: Session, + output: session::OutputStyle, + obj_filename: &Path, + out_filename: &Path, + lm: LinkMeta) { + let libname = output_lib_filename(lm); + let out_filename = match output { + session::OutputRlib => { + out_filename.with_filename(format!("lib{}.rlib", libname)) } - } - - - let output = if *sess.building_library { - let long_libname = output_dll_filename(sess.targ_cfg.os, lm); - out_filename.with_filename(long_libname) - } else { - out_filename.clone() + session::OutputDylib => { + let (prefix, suffix) = match sess.targ_cfg.os { + abi::OsWin32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX), + abi::OsMacos => (macos::DLL_PREFIX, macos::DLL_SUFFIX), + abi::OsLinux => (linux::DLL_PREFIX, linux::DLL_SUFFIX), + abi::OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX), + abi::OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX), + }; + out_filename.with_filename(format!("{}{}{}", prefix, libname, suffix)) + } + session::OutputStaticlib => { + out_filename.with_filename(format!("lib{}.a", libname)) + } + session::OutputExecutable => out_filename.clone(), }; // Make sure the output and obj_filename are both writeable. @@ -1006,61 +975,281 @@ fn unlib(config: @session::config, stem: ~str) -> ~str { // however, the Linux linker will happily overwrite a read-only file. // We should be consistent. let obj_is_writeable = is_writeable(obj_filename); - let out_is_writeable = is_writeable(&output); + let out_is_writeable = is_writeable(&out_filename); if !out_is_writeable { sess.fatal(format!("Output file {} is not writeable -- check its permissions.", - output.display())); + out_filename.display())); } else if !obj_is_writeable { sess.fatal(format!("Object file {} is not writeable -- check its permissions.", obj_filename.display())); } + match output { + session::OutputRlib => { + link_rlib(sess, obj_filename, &out_filename); + } + session::OutputStaticlib => { + link_staticlib(sess, obj_filename, &out_filename); + } + session::OutputExecutable => { + link_natively(sess, false, obj_filename, &out_filename); + } + session::OutputDylib => { + link_natively(sess, true, obj_filename, &out_filename); + } + } +} + +// Create an 'rlib' +// +// An rlib in its current incarnation is essentially a renamed .a file. The +// rlib primarily contains the object file of the crate, but it also contains +// all of the object files from native libraries. This is done by unzipping +// native libraries and inserting all of the contents into this archive. +fn link_rlib(sess: Session, obj_filename: &Path, + out_filename: &Path) -> Archive { + let mut a = Archive::create(sess, out_filename, obj_filename); + for &(ref l, kind) in cstore::get_used_libraries(sess.cstore).iter() { + match kind { + cstore::NativeStatic => { + a.add_native_library(l.as_slice()); + } + cstore::NativeUnknown => {} + } + } + return a; +} + +// Create a static archive +// +// This is essentially the same thing as an rlib, but it also involves adding +// all of the upstream crates' objects into the the archive. This will slurp in +// all of the native libraries of upstream dependencies as well. +// +// Additionally, there's no way for us to link dynamic libraries, so we warn +// about all dynamic library dependencies that they're not linked in. +fn link_staticlib(sess: Session, obj_filename: &Path, out_filename: &Path) { + let mut a = link_rlib(sess, obj_filename, out_filename); + a.add_native_library("morestack"); + + let crates = cstore::get_used_crates(sess.cstore, cstore::RequireStatic); + for &(cnum, ref path) in crates.iter() { + let p = match *path { + Some(ref p) => p.clone(), None => { + sess.err(format!("could not find rlib for: `{}`", + cstore::get_crate_data(sess.cstore, cnum).name)); + continue + } + }; + a.add_rlib(&p); + let native_libs = csearch::get_native_libraries(sess.cstore, cnum); + for lib in native_libs.iter() { + sess.warn(format!("unlinked native library: {}", *lib)); + } + } +} + +// Create a dynamic library or executable +// +// This will invoke the system linker/cc to create the resulting file. This +// links to all upstream files as well. +fn link_natively(sess: Session, dylib: bool, obj_filename: &Path, + out_filename: &Path) { + // The invocations of cc share some flags across platforms + let cc_prog = get_cc_prog(sess); + let mut cc_args = sess.targ_cfg.target_strs.cc_args.clone(); + cc_args.push_all_move(link_args(sess, dylib, obj_filename, out_filename)); + if (sess.opts.debugging_opts & session::print_link_args) != 0 { + println!("{} link args: {}", cc_prog, cc_args.connect(" ")); + } + + // May have not found libraries in the right formats. + sess.abort_if_errors(); + + // Invoke the system linker + debug!("{} {}", cc_prog, cc_args.connect(" ")); + let prog = run::process_output(cc_prog, cc_args); + + if !prog.status.success() { + sess.err(format!("linking with `{}` failed: {}", cc_prog, prog.status)); + sess.note(format!("{} arguments: {}", cc_prog, cc_args.connect(" "))); + sess.note(str::from_utf8(prog.error + prog.output)); + sess.abort_if_errors(); + } + + + // On OSX, debuggers need this utility to get run to do some munging of + // the symbols + if sess.targ_cfg.os == abi::OsMacos && sess.opts.debuginfo { + // FIXME (#9639): This needs to handle non-utf8 paths + run::process_status("dsymutil", + [out_filename.as_str().unwrap().to_owned()]); + } +} + +fn link_args(sess: Session, + dylib: bool, + obj_filename: &Path, + out_filename: &Path) -> ~[~str] { + // The default library location, we need this to find the runtime. // The location of crates will be determined as needed. // FIXME (#9639): This needs to handle non-utf8 paths let lib_path = sess.filesearch.get_target_lib_path(); let stage: ~str = ~"-L" + lib_path.as_str().unwrap(); - let mut args = vec::append(~[stage], sess.targ_cfg.target_strs.cc_args); + let mut args = ~[stage]; // FIXME (#9639): This needs to handle non-utf8 paths args.push_all([ - ~"-o", output.as_str().unwrap().to_owned(), + ~"-o", out_filename.as_str().unwrap().to_owned(), obj_filename.as_str().unwrap().to_owned()]); - let lib_cmd = match sess.targ_cfg.os { - abi::OsMacos => ~"-dynamiclib", - _ => ~"-shared" - }; + if sess.targ_cfg.os == abi::OsLinux { + // GNU-style linkers will use this to omit linking to libraries which + // don't actually fulfill any relocations, but only for libraries which + // follow this flag. Thus, use it before specifing libraries to link to. + args.push(~"-Wl,--as-needed"); - // # Crate linking - - let cstore = sess.cstore; - let r = cstore::get_used_crate_files(cstore); - // FIXME (#9639): This needs to handle non-utf8 paths - for cratepath in r.iter() { - if cratepath.extension_str() == Some("rlib") { - args.push(cratepath.as_str().unwrap().to_owned()); - continue; + // GNU-style linkers supports optimization with -O. --gc-sections + // removes metadata and potentially other useful things, so don't + // include it. GNU ld doesn't need a numeric argument, but other linkers + // do. + if sess.opts.optimize == session::Default || + sess.opts.optimize == session::Aggressive { + args.push(~"-Wl,-O1"); } - let dir = cratepath.dirname_str().unwrap(); - if !dir.is_empty() { args.push("-L" + dir); } - let libarg = unlib(sess.targ_cfg, cratepath.filestem_str().unwrap().to_owned()); - args.push("-l" + libarg); } - let ula = cstore::get_used_link_args(cstore); - for arg in ula.iter() { args.push(arg.to_owned()); } + add_upstream_rust_crates(&mut args, sess, dylib); + add_local_native_libraries(&mut args, sess); - // # Extern library linking + // # Telling the linker what we're doing - // User-supplied library search paths (-L on the cammand line) These are - // the same paths used to find Rust crates, so some of them may have been - // added already by the previous crate linking code. This only allows them - // to be found at compile time so it is still entirely up to outside - // forces to make sure that library can be found at runtime. + if dylib { + // On mac we need to tell the linker to let this library be rpathed + if sess.targ_cfg.os == abi::OsMacos { + args.push(~"-dynamiclib"); + args.push(~"-Wl,-dylib"); + // FIXME (#9639): This needs to handle non-utf8 paths + args.push(~"-Wl,-install_name,@rpath/" + + out_filename.filename_str().unwrap()); + } else { + args.push(~"-shared") + } + } + if sess.targ_cfg.os == abi::OsFreebsd { + args.push_all([~"-L/usr/local/lib", + ~"-L/usr/local/lib/gcc46", + ~"-L/usr/local/lib/gcc44"]); + } + + // Stack growth requires statically linking a __morestack function + args.push(~"-lmorestack"); + + // FIXME (#2397): At some point we want to rpath our guesses as to + // where extern libraries might live, based on the + // addl_lib_search_paths + args.push_all(rpath::get_rpath_flags(sess, out_filename)); + + // Finally add all the linker arguments provided on the command line along + // with any #[link_args] attributes found inside the crate + args.push_all(sess.opts.linker_args); + for arg in cstore::get_used_link_args(sess.cstore).iter() { + args.push(arg.clone()); + } + return args; +} + +// # Rust Crate linking +// +// Rust crates are not considered at all when creating an rlib output. All +// dependencies will be linked when producing the final output (instead of +// the intermediate rlib version) +fn add_upstream_rust_crates(args: &mut ~[~str], sess: Session, + dylib: bool) { + // Converts a library file-stem into a cc -l argument + fn unlib(config: @session::config, stem: &str) -> ~str { + if stem.starts_with("lib") && + config.os != abi::OsWin32 { + stem.slice(3, stem.len()).to_owned() + } else { + stem.to_owned() + } + } + + let cstore = sess.cstore; + if !dylib && !sess.prefer_dynamic() { + // With an executable, things get a little interesting. As a limitation + // of the current implementation, we require that everything must be + // static, or everything must be dynamic. The reasons for this are a + // little subtle, but as with the above two cases, the goal is to + // prevent duplicate copies of the same library showing up. For example, + // a static immediate dependency might show up as an upstream dynamic + // dependency and we currently have no way of knowing that. We know that + // all dynamic libaries require dynamic dependencies (see above), so + // it's satisfactory to include either all static libraries or all + // dynamic libraries. + let crates = cstore::get_used_crates(cstore, + cstore::RequireStatic); + if crates.iter().all(|&(_, ref p)| p.is_some()) { + for &(cnum, ref path) in crates.iter() { + let cratepath = path.clone().unwrap(); + + // If we're linking to the static version of the crate, then + // we're mostly good to go. The caveat here is that we need to + // pull in the static crate's native dependencies. + args.push(cratepath.as_str().unwrap().to_owned()); + + let libs = csearch::get_native_libraries(sess.cstore, cnum); + for lib in libs.iter() { + args.push("-l" + *lib); + } + } + return; + } + } + + // This is a fallback of three differnet cases of linking: + // + // * When creating a dynamic library, all inputs are required to be dynamic + // as well + // * If an executable is created with a preference on dynamic linking, then + // this case is the fallback + // * If an executable is being created, and one of the inputs is missing as + // a static library, then this is the fallback case. + let crates = cstore::get_used_crates(cstore, cstore::RequireDynamic); + for &(cnum, ref path) in crates.iter() { + let cratepath = match *path { + Some(ref p) => p.clone(), None => { + sess.err(format!("could not find dynamic library for: `{}`", + cstore::get_crate_data(sess.cstore, cnum).name)); + return + } + }; + // Just need to tell the linker about where the library lives and what + // its name is + let dir = cratepath.dirname_str().unwrap(); + if !dir.is_empty() { args.push("-L" + dir); } + let libarg = unlib(sess.targ_cfg, cratepath.filestem_str().unwrap()); + args.push("-l" + libarg); + } +} + +// # Native library linking +// +// User-supplied library search paths (-L on the cammand line) These are +// the same paths used to find Rust crates, so some of them may have been +// added already by the previous crate linking code. This only allows them +// to be found at compile time so it is still entirely up to outside +// forces to make sure that library can be found at runtime. +// +// Also note that the native libraries linked here are only the ones located +// in the current crate. Upstream crates with native library dependencies +// may have their native library pulled in above. +fn add_local_native_libraries(args: &mut ~[~str], sess: Session) { for path in sess.opts.addl_lib_search_paths.iter() { // FIXME (#9639): This needs to handle non-utf8 paths args.push("-L" + path.as_str().unwrap().to_owned()); @@ -1072,73 +1261,7 @@ fn unlib(config: @session::config, stem: ~str) -> ~str { args.push("-L" + path.as_str().unwrap().to_owned()); } - if sess.targ_cfg.os == abi::OsLinux { - // GNU-style linkers will use this to omit linking to libraries which don't actually fulfill - // any relocations, but only for libraries which follow this flag. Thus, use it before - // specifing libraries to link to. - args.push(~"-Wl,--as-needed"); + for &(ref l, _) in cstore::get_used_libraries(sess.cstore).iter() { + args.push(~"-l" + *l); } - - // The names of the extern libraries - let used_libs = cstore::get_used_libraries(cstore); - for l in used_libs.iter() { args.push(~"-l" + *l); } - - if *sess.building_library { - args.push(lib_cmd); - - // On mac we need to tell the linker to let this library - // be rpathed - if sess.targ_cfg.os == abi::OsMacos { - // FIXME (#9639): This needs to handle non-utf8 paths - args.push("-Wl,-install_name,@rpath/" - + output.filename_str().unwrap()); - } - } - - // On linux librt and libdl are an indirect dependencies via rustrt, - // and binutils 2.22+ won't add them automatically - if sess.targ_cfg.os == abi::OsLinux { - // GNU-style linkers supports optimization with -O. --gc-sections removes metadata and - // potentially other useful things, so don't include it. GNU ld doesn't need a numeric - // argument, but other linkers do. - if sess.opts.optimize == session::Default || sess.opts.optimize == session::Aggressive { - args.push(~"-Wl,-O1"); - } - - args.push_all([~"-lrt", ~"-ldl"]); - - // LLVM implements the `frem` instruction as a call to `fmod`, - // which lives in libm. Similar to above, on some linuxes we - // have to be explicit about linking to it. See #2510 - args.push(~"-lm"); - } - else if sess.targ_cfg.os == abi::OsAndroid { - args.push_all([~"-ldl", ~"-llog", ~"-lsupc++", ~"-lgnustl_shared"]); - args.push(~"-lm"); - } - - if sess.targ_cfg.os == abi::OsFreebsd { - args.push_all([~"-pthread", ~"-lrt", - ~"-L/usr/local/lib", ~"-lexecinfo", - ~"-L/usr/local/lib/gcc46", - ~"-L/usr/local/lib/gcc44", ~"-lstdc++", - ~"-Wl,-z,origin", - ~"-Wl,-rpath,/usr/local/lib/gcc46", - ~"-Wl,-rpath,/usr/local/lib/gcc44"]); - } - - // Stack growth requires statically linking a __morestack function - args.push(~"-lmorestack"); - - // Always want the runtime linked in - args.push(~"-lrustrt"); - - // FIXME (#2397): At some point we want to rpath our guesses as to where - // extern libraries might live, based on the addl_lib_search_paths - args.push_all(rpath::get_rpath_flags(sess, &output)); - - // Finally add all the linker arguments provided on the command line - args.push_all(sess.opts.linker_args); - - return args; } diff --git a/src/librustc/back/mips.rs b/src/librustc/back/mips.rs index cbe39efdf8c..ce716f6f94b 100644 --- a/src/librustc/back/mips.rs +++ b/src/librustc/back/mips.rs @@ -63,6 +63,6 @@ pub fn get_target_strs(target_triple: ~str, target_os: abi::Os) -> target_strs:: target_triple: target_triple, - cc_args: ~[] + cc_args: ~[], }; } diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs index 5f01a57c23f..0853213a529 100644 --- a/src/librustc/back/rpath.rs +++ b/src/librustc/back/rpath.rs @@ -21,8 +21,7 @@ fn not_win32(os: abi::Os) -> bool { os != abi::OsWin32 } -pub fn get_rpath_flags(sess: session::Session, out_filename: &Path) - -> ~[~str] { +pub fn get_rpath_flags(sess: session::Session, out_filename: &Path) -> ~[~str] { let os = sess.targ_cfg.os; // No rpath on windows @@ -30,18 +29,28 @@ pub fn get_rpath_flags(sess: session::Session, out_filename: &Path) return ~[]; } + let mut flags = ~[]; + + if sess.targ_cfg.os == abi::OsFreebsd { + flags.push_all([~"-Wl,-rpath,/usr/local/lib/gcc46", + ~"-Wl,-rpath,/usr/local/lib/gcc44", + ~"-Wl,-z,origin"]); + } + debug!("preparing the RPATH!"); let sysroot = sess.filesearch.sysroot(); let output = out_filename; - let libs = cstore::get_used_crate_files(sess.cstore); + let libs = cstore::get_used_crates(sess.cstore, cstore::RequireDynamic); + let libs = libs.move_iter().filter_map(|(_, l)| l.map(|p| p.clone())).collect(); // We don't currently rpath extern libraries, but we know // where rustrt is and we know every rust program needs it let libs = vec::append_one(libs, get_sysroot_absolute_rt_lib(sess)); let rpaths = get_rpaths(os, sysroot, output, libs, sess.opts.target_triple); - rpaths_to_flags(rpaths) + flags.push_all(rpaths_to_flags(rpaths)); + flags } fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path { @@ -52,8 +61,11 @@ fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path { } pub fn rpaths_to_flags(rpaths: &[~str]) -> ~[~str] { - // FIXME (#9639): This needs to handle non-utf8 paths - rpaths.iter().map(|rpath| format!("-Wl,-rpath,{}",*rpath)).collect() + let mut ret = ~[]; + for rpath in rpaths.iter() { + ret.push("-Wl,-rpath," + *rpath); + } + return ret; } fn get_rpaths(os: abi::Os, diff --git a/src/librustc/back/target_strs.rs b/src/librustc/back/target_strs.rs index 7baaac4fc95..87133237878 100644 --- a/src/librustc/back/target_strs.rs +++ b/src/librustc/back/target_strs.rs @@ -14,5 +14,5 @@ pub struct t { meta_sect_name: ~str, data_layout: ~str, target_triple: ~str, - cc_args: ~[~str] + cc_args: ~[~str], } diff --git a/src/librustc/back/x86.rs b/src/librustc/back/x86.rs index b3c92bc4057..de0372b83b9 100644 --- a/src/librustc/back/x86.rs +++ b/src/librustc/back/x86.rs @@ -46,6 +46,6 @@ pub fn get_target_strs(target_triple: ~str, target_os: abi::Os) -> target_strs:: target_triple: target_triple, - cc_args: ~[~"-m32"] + cc_args: ~[~"-m32"], }; } diff --git a/src/librustc/back/x86_64.rs b/src/librustc/back/x86_64.rs index 3237085dfb9..dce4de3dce3 100644 --- a/src/librustc/back/x86_64.rs +++ b/src/librustc/back/x86_64.rs @@ -54,6 +54,6 @@ pub fn get_target_strs(target_triple: ~str, target_os: abi::Os) -> target_strs:: target_triple: target_triple, - cc_args: ~[~"-m64"] + cc_args: ~[~"-m64"], }; } diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 8821f5f6229..7d13cb2da65 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -11,7 +11,7 @@ use back::link; use back::{arm, x86, x86_64, mips}; -use driver::session::{Aggressive}; +use driver::session::{Aggressive, OutputExecutable}; use driver::session::{Session, Session_, No, Less, Default}; use driver::session; use front; @@ -164,8 +164,11 @@ pub fn phase_2_configure_and_expand(sess: Session, mut crate: ast::Crate) -> ast::Crate { let time_passes = sess.time_passes(); - *sess.building_library = session::building_library(sess.opts.crate_type, - &crate, sess.opts.test); + *sess.building_library = session::building_library(sess.opts, &crate); + let want_exe = sess.opts.outputs.iter().any(|&o| o == OutputExecutable); + if *sess.building_library && want_exe { + sess.err("cannot build both a library and an executable"); + } time(time_passes, "gated feature checking", (), |_| front::feature_gate::check_crate(sess, &crate)); @@ -225,10 +228,8 @@ pub fn phase_3_run_analysis_passes(sess: Session, syntax::ast_map::map_crate(sess.diagnostic(), crate)); time(time_passes, "external crate/lib resolution", (), |_| - creader::read_crates(sess.diagnostic(), crate, sess.cstore, - sess.filesearch, + creader::read_crates(sess, crate, session::sess_os_to_meta_os(sess.targ_cfg.os), - sess.opts.is_static, token::get_ident_interner())); let lang_items = time(time_passes, "language item collection", (), |_| @@ -330,7 +331,8 @@ pub fn phase_3_run_analysis_passes(sess: Session, pub struct CrateTranslation { context: ContextRef, module: ModuleRef, - link: LinkMeta + link: LinkMeta, + crate_types: ~[~str], } /// Run the translation phase to LLVM, after which the AST and analysis can @@ -390,6 +392,7 @@ pub fn phase_6_link_output(sess: Session, outputs: &OutputFilenames) { time(sess.time_passes(), "linking", (), |_| link::link_binary(sess, + trans.crate_types, &outputs.obj_filename, &outputs.out_filename, trans.link)); @@ -417,11 +420,6 @@ pub fn stop_after_phase_5(sess: Session) -> bool { return true; } - if sess.opts.is_static && *sess.building_library { - debug!("building static library, returning early from compile_input"); - return true; - } - if sess.opts.jit { debug!("running JIT, returning early from compile_input"); return true; @@ -652,13 +650,21 @@ pub fn build_session_options(binary: @str, matches: &getopts::Matches, demitter: @diagnostic::Emitter) -> @session::options { - let crate_type = if matches.opt_present("lib") { - session::lib_crate - } else if matches.opt_present("bin") { - session::bin_crate - } else { - session::unknown_crate - }; + let mut outputs = ~[]; + if matches.opt_present("rlib") { + outputs.push(session::OutputRlib) + } + if matches.opt_present("staticlib") { + outputs.push(session::OutputStaticlib) + } + // dynamic libraries are the "compiler blesssed" default library + if matches.opt_present("dylib") || matches.opt_present("lib") { + outputs.push(session::OutputDylib) + } + if matches.opt_present("bin") { + outputs.push(session::OutputExecutable) + } + let parse_only = matches.opt_present("parse-only"); let no_trans = matches.opt_present("no-trans"); @@ -750,11 +756,10 @@ pub fn build_session_options(binary: @str, let debuginfo = debugging_opts & session::debug_info != 0 || extra_debuginfo; - let statik = debugging_opts & session::statik != 0; - let addl_lib_search_paths = matches.opt_strs("L").map(|s| { - Path::init(s.as_slice()) + Path::init(s.as_slice()) }).move_iter().collect(); + let ar = matches.opt_str("ar"); let linker = matches.opt_str("linker"); let linker_args = matches.opt_strs("link-args").flat_map( |a| { a.split(' ').map(|arg| arg.to_owned()).collect() @@ -782,8 +787,7 @@ pub fn build_session_options(binary: @str, }; let sopts = @session::options { - crate_type: crate_type, - is_static: statik, + outputs: outputs, gc: gc, optimize: opt_level, custom_passes: custom_passes, @@ -795,6 +799,7 @@ pub fn build_session_options(binary: @str, jit: jit, output_type: output_type, addl_lib_search_paths: @mut addl_lib_search_paths, + ar: ar, linker: linker, linker_args: linker_args, maybe_sysroot: sysroot_opt, @@ -871,7 +876,6 @@ pub fn parse_pretty(sess: Session, name: &str) -> PpMode { // rustc command line options pub fn optgroups() -> ~[getopts::groups::OptGroup] { ~[ - optflag("", "bin", "Compile an executable crate (default)"), optflag("c", "", "Compile and assemble, but do not link"), optmulti("", "cfg", "Configure the compilation environment", "SPEC"), @@ -881,8 +885,13 @@ pub fn optgroups() -> ~[getopts::groups::OptGroup] { optflag("h", "help","Display this message"), optmulti("L", "", "Add a directory to the library search path", "PATH"), - optflag("", "lib", "Compile a library crate"), + optflag("", "bin", "Compile an executable crate (default)"), + optflag("", "lib", "Compile a rust library crate using the compiler's default"), + optflag("", "rlib", "Compile a rust library crate as an rlib file"), + optflag("", "staticlib", "Compile a static library crate"), + optflag("", "dylib", "Compile a dynamic library crate"), optopt("", "linker", "Program to use for linking instead of the default.", "LINKER"), + optopt("", "ar", "Program to use for managing archives instead of the default.", "AR"), optmulti("", "link-args", "FLAGS is a space-separated list of flags passed to the linker", "FLAGS"), optflag("", "ls", "List the symbols defined by a library crate"), @@ -957,19 +966,16 @@ pub fn build_output_filenames(input: &input, let obj_path; let out_path; let sopts = sess.opts; - let stop_after_codegen = - sopts.output_type != link::output_type_exe || - sopts.is_static && *sess.building_library; + let stop_after_codegen = sopts.output_type != link::output_type_exe; - let obj_suffix = - match sopts.output_type { - link::output_type_none => ~"none", - link::output_type_bitcode => ~"bc", - link::output_type_assembly => ~"s", - link::output_type_llvm_assembly => ~"ll", - // Object and exe output both use the '.o' extension here - link::output_type_object | link::output_type_exe => ~"o" - }; + let obj_suffix = match sopts.output_type { + link::output_type_none => ~"none", + link::output_type_bitcode => ~"bc", + link::output_type_assembly => ~"s", + link::output_type_llvm_assembly => ~"ll", + // Object and exe output both use the '.o' extension here + link::output_type_object | link::output_type_exe => ~"o" + }; match *ofile { None => { @@ -1047,6 +1053,7 @@ pub fn early_error(emitter: @diagnostic::Emitter, msg: &str) -> ! { pub fn list_metadata(sess: Session, path: &Path, out: @mut io::Writer) { metadata::loader::list_file_metadata( + sess, token::get_ident_interner(), session::sess_os_to_meta_os(sess.targ_cfg.os), path, out); } diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index e497caa4946..ba9e8449418 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -13,7 +13,6 @@ use back::target_strs; use back; use driver::driver::host_triple; -use driver::session; use metadata::filesearch; use metadata; use middle::lint; @@ -30,13 +29,6 @@ use std::hashmap::{HashMap,HashSet}; -#[deriving(Clone)] -pub enum crate_type { - bin_crate, - lib_crate, - unknown_crate, -} - pub struct config { os: abi::Os, arch: abi::Architecture, @@ -66,16 +58,16 @@ pub struct config { pub static jit: uint = 1 << 18; pub static debug_info: uint = 1 << 19; pub static extra_debug_info: uint = 1 << 20; -pub static statik: uint = 1 << 21; -pub static print_link_args: uint = 1 << 22; -pub static no_debug_borrows: uint = 1 << 23; -pub static lint_llvm: uint = 1 << 24; -pub static print_llvm_passes: uint = 1 << 25; -pub static no_vectorize_loops: uint = 1 << 26; -pub static no_vectorize_slp: uint = 1 << 27; -pub static no_prepopulate_passes: uint = 1 << 28; -pub static use_softfp: uint = 1 << 29; -pub static gen_crate_map: uint = 1 << 30; +pub static print_link_args: uint = 1 << 21; +pub static no_debug_borrows: uint = 1 << 22; +pub static lint_llvm: uint = 1 << 23; +pub static print_llvm_passes: uint = 1 << 24; +pub static no_vectorize_loops: uint = 1 << 25; +pub static no_vectorize_slp: uint = 1 << 26; +pub static no_prepopulate_passes: uint = 1 << 27; +pub static use_softfp: uint = 1 << 28; +pub static gen_crate_map: uint = 1 << 29; +pub static prefer_dynamic: uint = 1 << 30; pub fn debugging_opts_map() -> ~[(&'static str, &'static str, uint)] { ~[("verbose", "in general, enable more debug printouts", verbose), @@ -107,7 +99,6 @@ pub fn debugging_opts_map() -> ~[(&'static str, &'static str, uint)] { ("extra-debug-info", "Extra debugging info (experimental)", extra_debug_info), ("debug-info", "Produce debug info (experimental)", debug_info), - ("static", "Use or produce static libraries or binaries (experimental)", statik), ("no-debug-borrows", "do not show where borrow checks fail", no_debug_borrows), @@ -129,6 +120,7 @@ pub fn debugging_opts_map() -> ~[(&'static str, &'static str, uint)] { no_vectorize_slp), ("soft-float", "Generate software floating point library calls", use_softfp), ("gen-crate-map", "Force generation of a toplevel crate map", gen_crate_map), + ("prefer-dynamic", "Prefer dynamic linking to static linking", prefer_dynamic), ] } @@ -144,8 +136,8 @@ pub enum OptLevel { pub struct options { // The crate config requested for the session, which may be combined // with additional crate configurations during the compile process - crate_type: crate_type, - is_static: bool, + outputs: ~[OutputStyle], + gc: bool, optimize: OptLevel, custom_passes: ~[~str], @@ -159,6 +151,7 @@ pub struct options { addl_lib_search_paths: @mut HashSet, // This is mutable for rustpkg, which // updates search paths based on the // parsed code + ar: Option<~str>, linker: Option<~str>, linker_args: ~[~str], maybe_sysroot: Option<@Path>, @@ -194,6 +187,14 @@ pub enum EntryFnType { EntryNone, } +#[deriving(Eq, Clone)] +pub enum OutputStyle { + OutputExecutable, + OutputDylib, + OutputRlib, + OutputStaticlib, +} + pub struct Session_ { targ_cfg: @config, opts: @options, @@ -337,6 +338,9 @@ pub fn no_vectorize_slp(&self) -> bool { pub fn gen_crate_map(&self) -> bool { self.debugging_opt(gen_crate_map) } + pub fn prefer_dynamic(&self) -> bool { + self.debugging_opt(prefer_dynamic) + } // pointless function, now... pub fn str_of(&self, id: ast::Ident) -> @str { @@ -357,8 +361,7 @@ pub fn intr(&self) -> @syntax::parse::token::ident_interner { /// Some reasonable defaults pub fn basic_options() -> @options { @options { - crate_type: session::lib_crate, - is_static: false, + outputs: ~[], gc: false, optimize: No, custom_passes: ~[], @@ -370,6 +373,7 @@ pub fn basic_options() -> @options { jit: false, output_type: link::output_type_exe, addl_lib_search_paths: @mut HashSet::new(), + ar: None, linker: None, linker_args: ~[], maybe_sysroot: None, @@ -391,24 +395,17 @@ pub fn expect(sess: Session, opt: Option, msg: || -> ~str) -> T { diagnostic::expect(sess.diagnostic(), opt, msg) } -pub fn building_library(req_crate_type: crate_type, - crate: &ast::Crate, - testing: bool) -> bool { - match req_crate_type { - bin_crate => false, - lib_crate => true, - unknown_crate => { - if testing { - false - } else { - match syntax::attr::first_attr_value_str_by_name( - crate.attrs, - "crate_type") { - Some(s) => "lib" == s, - _ => false - } +pub fn building_library(options: &options, crate: &ast::Crate) -> bool { + for output in options.outputs.iter() { + match *output { + OutputExecutable => {} + OutputStaticlib | OutputDylib | OutputRlib => return true } - } + } + if options.test { return false } + match syntax::attr::first_attr_value_str_by_name(crate.attrs, "crate_type") { + Some(s) => "lib" == s || "rlib" == s || "dylib" == s || "staticlib" == s, + _ => false } } diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 82ddb9c2f97..3cff0b81e87 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -21,6 +21,7 @@ use middle::lint; use syntax::ast; +use syntax::attr; use syntax::attr::AttrMetaMethods; use syntax::codemap::Span; use syntax::visit; @@ -41,6 +42,7 @@ ("managed_boxes", Active), ("non_ascii_idents", Active), ("thread_local", Active), + ("link_args", Active), // These are used to test this portion of the compiler, they don't actually // mean anything @@ -111,7 +113,8 @@ fn visit_view_item(&mut self, i: &ast::view_item, _: ()) { fn visit_item(&mut self, i: @ast::item, _:()) { for attr in i.attrs.iter() { - if "thread_local" == attr.name() { + if "thread_local" == attr.name() && + cfg!(stage0, remove_this_on_next_snapshot) { // NOTE: snap rem self.gate_feature("thread_local", i.span, "`#[thread_local]` is an experimental feature, and does not \ currently handle destructors. There is no corresponding \ @@ -132,6 +135,16 @@ fn visit_item(&mut self, i: @ast::item, _:()) { } } + ast::item_foreign_mod(*) => { + if attr::contains_name(i.attrs, "link_args") && + cfg!(stage0, remove_this_on_next_snapshot) { // NOTE: snap + self.gate_feature("link_args", i.span, + "the `link_args` attribute is not portable \ + across platforms, it is recommended to \ + use `#[link(name = \"foo\")]` instead") + } + } + _ => {} } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index b5703a62a1f..90a005568b6 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -16,7 +16,8 @@ #[comment = "The Rust compiler"]; #[license = "MIT/ASL2"]; -#[crate_type = "lib"]; +#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot +#[crate_type = "dylib"]; #[feature(macro_rules, globs, struct_variant, managed_boxes)]; @@ -87,6 +88,7 @@ pub mod front { } pub mod back { + pub mod archive; pub mod link; pub mod abi; pub mod upcall; diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs index a6a96b25ff5..5e5cc242358 100644 --- a/src/librustc/lib/llvm.rs +++ b/src/librustc/lib/llvm.rs @@ -14,7 +14,6 @@ use std::hashmap::HashMap; use std::libc::{c_uint, c_ushort, c_void, free}; use std::str::raw::from_c_str; -use std::option; use middle::trans::type_::Type; @@ -304,9 +303,16 @@ pub mod llvm { use super::{ValueRef, TargetMachineRef, FileType}; use super::{CodeGenModel, RelocMode, CodeGenOptLevel}; use super::debuginfo::*; - use std::libc::{c_char, c_int, c_longlong, c_ushort, c_uint, c_ulonglong}; + use std::libc::{c_char, c_int, c_longlong, c_ushort, c_uint, c_ulonglong, + size_t}; + #[cfg(stage0)] #[link_args = "-lrustllvm"] + extern {} + #[cfg(not(stage0))] // if you're deleting this, put this on the block below + #[link(name = "rustllvm")] + extern {} + extern { /* Create and destroy contexts. */ pub fn LLVMContextCreate() -> ContextRef; @@ -1426,6 +1432,16 @@ pub fn LLVMIsSectionIteratorAtEnd(ObjFile: ObjectFileRef, LLVMDisposeMemoryBuffer() to get rid of it. */ pub fn LLVMRustCreateMemoryBufferWithContentsOfFile(Path: *c_char) -> MemoryBufferRef; + /** Borrows the contents of the memory buffer (doesn't copy it) */ + pub fn LLVMCreateMemoryBufferWithMemoryRange(InputData: *c_char, + InputDataLength: size_t, + BufferName: *c_char, + RequiresNull: Bool) + -> MemoryBufferRef; + pub fn LLVMCreateMemoryBufferWithMemoryRangeCopy(InputData: *c_char, + InputDataLength: size_t, + BufferName: *c_char) + -> MemoryBufferRef; /** Returns a string describing the last error caused by an LLVMRust* call. */ @@ -1901,38 +1917,32 @@ pub fn mk_pass_manager() -> PassManager { /* Memory-managed interface to object files. */ -pub struct object_file_res { - ObjectFile: ObjectFileRef, +pub struct ObjectFile { + llof: ObjectFileRef, } -impl Drop for object_file_res { - fn drop(&mut self) { +impl ObjectFile { + // This will take ownership of llmb + pub fn new(llmb: MemoryBufferRef) -> Option { unsafe { - llvm::LLVMDisposeObjectFile(self.ObjectFile); + let llof = llvm::LLVMCreateObjectFile(llmb); + if llof as int == 0 { + llvm::LLVMDisposeMemoryBuffer(llmb); + return None + } + + Some(ObjectFile { + llof: llof, + }) } } } -pub fn object_file_res(ObjFile: ObjectFileRef) -> object_file_res { - object_file_res { - ObjectFile: ObjFile - } -} - -pub struct ObjectFile { - llof: ObjectFileRef, - dtor: @object_file_res -} - -pub fn mk_object_file(llmb: MemoryBufferRef) -> Option { - unsafe { - let llof = llvm::LLVMCreateObjectFile(llmb); - if llof as int == 0 { return option::None::; } - - option::Some(ObjectFile { - llof: llof, - dtor: @object_file_res(llof) - }) +impl Drop for ObjectFile { + fn drop(&mut self) { + unsafe { + llvm::LLVMDisposeObjectFile(self.llof); + } } } diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 5e6d0f27615..5ed1eac746c 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -197,6 +197,8 @@ pub fn from_uint(value : uint) -> Option { pub static tag_region_param_def_ident: uint = 0x101; pub static tag_region_param_def_def_id: uint = 0x102; +pub static tag_native_libraries: uint = 0x103; +pub static tag_native_libraries_lib: uint = 0x104; pub struct LinkMeta { name: @str, diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index f18357999a1..1f10699b765 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -10,10 +10,9 @@ //! Validates all used crates and extern libraries and loads their metadata - +use driver::session::Session; use metadata::cstore; use metadata::decoder; -use metadata::filesearch::FileSearch; use metadata::loader; use std::hashmap::HashMap; @@ -29,19 +28,13 @@ // Traverses an AST, reading all the information about use'd crates and extern // libraries necessary for later resolving, typechecking, linking, etc. -pub fn read_crates(diag: @mut span_handler, +pub fn read_crates(sess: Session, crate: &ast::Crate, - cstore: @mut cstore::CStore, - filesearch: @FileSearch, os: loader::Os, - statik: bool, intr: @ident_interner) { let e = @mut Env { - diag: diag, - filesearch: filesearch, - cstore: cstore, + sess: sess, os: os, - statik: statik, crate_cache: @mut ~[], next_crate_num: 1, intr: intr @@ -50,7 +43,7 @@ pub fn read_crates(diag: @mut span_handler, visit_crate(e, crate); visit::walk_crate(&mut v, crate, ()); dump_crates(*e.crate_cache); - warn_if_multiple_versions(e, diag, *e.crate_cache); + warn_if_multiple_versions(e, sess.diagnostic(), *e.crate_cache); } struct ReadCrateVisitor { e:@mut Env } @@ -113,18 +106,15 @@ fn warn_if_multiple_versions(e: @mut Env, } struct Env { - diag: @mut span_handler, - filesearch: @FileSearch, - cstore: @mut cstore::CStore, + sess: Session, os: loader::Os, - statik: bool, crate_cache: @mut ~[cache_entry], next_crate_num: ast::CrateNum, intr: @ident_interner } fn visit_crate(e: &Env, c: &ast::Crate) { - let cstore = e.cstore; + let cstore = e.sess.cstore; for a in c.attrs.iter().filter(|m| "link_args" == m.name()) { match a.value_str() { @@ -146,7 +136,7 @@ fn visit_view_item(e: @mut Env, i: &ast::view_item) { let p_path = Path::init(p); match p_path.filestem_str() { None|Some("") => - e.diag.span_bug(i.span, "Bad package path in `extern mod` item"), + e.sess.span_bug(i.span, "Bad package path in `extern mod` item"), Some(s) => vec::append( ~[attr::mk_name_value_item_str(@"package_id", p), @@ -162,7 +152,7 @@ fn visit_view_item(e: @mut Env, i: &ast::view_item) { meta_items, @"", i.span); - cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum); + cstore::add_extern_mod_stmt_cnum(e.sess.cstore, id, cnum); } _ => () } @@ -170,60 +160,62 @@ fn visit_view_item(e: @mut Env, i: &ast::view_item) { fn visit_item(e: &Env, i: @ast::item) { match i.node { - ast::item_foreign_mod(ref fm) => { - if fm.abis.is_rust() || fm.abis.is_intrinsic() { - return; - } + ast::item_foreign_mod(ref fm) => { + if fm.abis.is_rust() || fm.abis.is_intrinsic() { + return; + } - let cstore = e.cstore; - let link_args = i.attrs.iter() - .filter_map(|at| if "link_args" == at.name() {Some(at)} else {None}) - .collect::<~[&ast::Attribute]>(); - - // XXX: two whom it may concern, this was the old logic applied to the - // ast's extern mod blocks which had names (we used to allow - // "extern mod foo"). This code was never run for anonymous blocks, - // and we now only have anonymous blocks. We're still in the midst - // of figuring out what the exact operations we'd like to support - // when linking external modules, but I wanted to leave this logic - // here for the time beging to refer back to it - - //let mut already_added = false; - //let link_name = i.attrs.iter() - // .find(|at| "link_name" == at.name()) - // .and_then(|at| at.value_str()); - - //let foreign_name = match link_name { - // Some(nn) => { - // if nn.is_empty() { - // e.diag.span_fatal( - // i.span, - // "empty #[link_name] not allowed; use \ - // #[nolink]."); - // } - // nn - // } - // None => token::ident_to_str(&i.ident) - // }; - //if !attr::contains_name(i.attrs, "nolink") { - // already_added = - // !cstore::add_used_library(cstore, foreign_name); - //} - //if !link_args.is_empty() && already_added { - // e.diag.span_fatal(i.span, ~"library '" + foreign_name + - // "' already added: can't specify link_args."); - //} - - for m in link_args.iter() { - match m.value_str() { - Some(linkarg) => { - cstore::add_used_link_args(cstore, linkarg); + // First, add all of the custom link_args attributes + let cstore = e.sess.cstore; + let link_args = i.attrs.iter() + .filter_map(|at| if "link_args" == at.name() {Some(at)} else {None}) + .to_owned_vec(); + for m in link_args.iter() { + match m.value_str() { + Some(linkarg) => { + cstore::add_used_link_args(cstore, linkarg); + } + None => { /* fallthrough */ } + } + } + + // Next, process all of the #[link(..)]-style arguments + let cstore = e.sess.cstore; + let link_args = i.attrs.iter() + .filter_map(|at| if "link" == at.name() {Some(at)} else {None}) + .to_owned_vec(); + for m in link_args.iter() { + match m.meta_item_list() { + Some(items) => { + let kind = do items.iter().find |k| { + "kind" == k.name() + }.and_then(|a| a.value_str()); + let kind = match kind { + Some(k) if "static" == k => cstore::NativeStatic, + Some(k) => { + e.sess.span_fatal(i.span, + format!("unknown kind: `{}`", k)); + } + None => cstore::NativeUnknown + }; + let n = do items.iter().find |n| { + "name" == n.name() + }.and_then(|a| a.value_str()); + let n = match n { + Some(n) => n, + None => { + e.sess.span_fatal(i.span, + "#[link(...)] specified without \ + `name = \"foo\"`"); + } + }; + cstore::add_used_library(cstore, n.to_owned(), kind); + } + None => {} } - None => { /* fallthrough */ } } } - } - _ => { } + _ => { } } } @@ -263,24 +255,21 @@ fn resolve_crate(e: @mut Env, match existing_match(e, metas, hash) { None => { let load_ctxt = loader::Context { - diag: e.diag, - filesearch: e.filesearch, + sess: e.sess, span: span, ident: ident, metas: metas, hash: hash, os: e.os, - is_static: e.statik, intr: e.intr }; - let (lident, ldata) = loader::load_library_crate(&load_ctxt); + let loader::Library { + dylib, rlib, metadata + } = load_ctxt.load_library_crate(); - let cfilename = Path::init(lident); - let cdata = ldata; - - let attrs = decoder::get_crate_attributes(cdata); + let attrs = decoder::get_crate_attributes(metadata); let linkage_metas = attr::find_linkage_metas(attrs); - let hash = decoder::get_crate_hash(cdata); + let hash = decoder::get_crate_hash(metadata); // Claim this crate number and cache it let cnum = e.next_crate_num; @@ -293,7 +282,7 @@ fn resolve_crate(e: @mut Env, e.next_crate_num += 1; // Now resolve the crates referenced by this crate - let cnum_map = resolve_crate_deps(e, cdata); + let cnum_map = resolve_crate_deps(e, metadata); let cname = match attr::last_meta_item_value_str_by_name(load_ctxt.metas, @@ -303,14 +292,18 @@ fn resolve_crate(e: @mut Env, }; let cmeta = @cstore::crate_metadata { name: cname, - data: cdata, + data: metadata, cnum_map: cnum_map, cnum: cnum }; - let cstore = e.cstore; + let cstore = e.sess.cstore; cstore::set_crate_data(cstore, cnum, cmeta); - cstore::add_used_crate_file(cstore, &cfilename); + cstore::add_used_crate_source(cstore, cstore::CrateSource { + dylib: dylib, + rlib: rlib, + cnum: cnum, + }); return cnum; } Some(cnum) => { diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs index 9c9dba52453..96250fd5ec8 100644 --- a/src/librustc/metadata/csearch.rs +++ b/src/librustc/metadata/csearch.rs @@ -262,6 +262,12 @@ pub fn get_item_visibility(cstore: @mut cstore::CStore, decoder::get_item_visibility(cdata, def_id.node) } +pub fn get_native_libraries(cstore: @mut cstore::CStore, + crate_num: ast::CrateNum) -> ~[~str] { + let cdata = cstore::get_crate_data(cstore, crate_num); + decoder::get_native_libraries(cdata) +} + pub fn each_impl(cstore: @mut cstore::CStore, crate_num: ast::CrateNum, callback: |ast::DefId|) { diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs index bc15312d98e..50da84d3895 100644 --- a/src/librustc/metadata/cstore.rs +++ b/src/librustc/metadata/cstore.rs @@ -34,12 +34,33 @@ pub struct crate_metadata { cnum: ast::CrateNum } +#[deriving(Eq)] +pub enum LinkagePreference { + RequireDynamic, + RequireStatic, +} + +#[deriving(Eq)] +pub enum NativeLibaryKind { + NativeStatic, + NativeUnknown, +} + +// Where a crate came from on the local filesystem. One of these two options +// must be non-None. +#[deriving(Eq)] +pub struct CrateSource { + dylib: Option, + rlib: Option, + cnum: ast::CrateNum, +} + pub struct CStore { priv metas: HashMap , priv extern_mod_crate_map: extern_mod_crate_map, - priv used_crate_files: ~[Path], - priv used_libraries: ~[@str], - priv used_link_args: ~[@str], + priv used_crate_sources: ~[CrateSource], + priv used_libraries: ~[(~str, NativeLibaryKind)], + priv used_link_args: ~[~str], intr: @ident_interner } @@ -50,7 +71,7 @@ pub fn mk_cstore(intr: @ident_interner) -> CStore { return CStore { metas: HashMap::new(), extern_mod_crate_map: HashMap::new(), - used_crate_files: ~[], + used_crate_sources: ~[], used_libraries: ~[], used_link_args: ~[], intr: intr @@ -88,39 +109,50 @@ pub fn iter_crate_data(cstore: &CStore, i: |ast::CrateNum, @crate_metadata|) { } } -pub fn add_used_crate_file(cstore: &mut CStore, lib: &Path) { - if !cstore.used_crate_files.contains(lib) { - cstore.used_crate_files.push((*lib).clone()); +pub fn add_used_crate_source(cstore: &mut CStore, src: CrateSource) { + if !cstore.used_crate_sources.contains(&src) { + cstore.used_crate_sources.push(src); } } -pub fn get_used_crate_files(cstore: &CStore) -> ~[Path] { - // XXX(pcwalton): Bad copy. - return cstore.used_crate_files.clone(); +pub fn get_used_crate_sources<'a>(cstore: &'a CStore) -> &'a [CrateSource] { + cstore.used_crate_sources.as_slice() } -pub fn add_used_library(cstore: &mut CStore, lib: @str) -> bool { +pub fn get_used_crates(cstore: &CStore, prefer: LinkagePreference) + -> ~[(ast::CrateNum, Option)] +{ + let mut ret = ~[]; + for src in cstore.used_crate_sources.iter() { + ret.push((src.cnum, match prefer { + RequireDynamic => src.dylib.clone(), + RequireStatic => src.rlib.clone(), + })); + } + return ret; +} + +pub fn add_used_library(cstore: &mut CStore, + lib: ~str, kind: NativeLibaryKind) -> bool { assert!(!lib.is_empty()); - if cstore.used_libraries.iter().any(|x| x == &lib) { return false; } - cstore.used_libraries.push(lib); + if cstore.used_libraries.iter().any(|&(ref x, _)| x == &lib) { return false; } + cstore.used_libraries.push((lib, kind)); true } -pub fn get_used_libraries<'a>(cstore: &'a CStore) -> &'a [@str] { - let slice: &'a [@str] = cstore.used_libraries; - slice +pub fn get_used_libraries<'a>(cstore: &'a CStore) -> &'a [(~str, NativeLibaryKind)] { + cstore.used_libraries.as_slice() } pub fn add_used_link_args(cstore: &mut CStore, args: &str) { for s in args.split(' ') { - cstore.used_link_args.push(s.to_managed()); + cstore.used_link_args.push(s.to_owned()); } } -pub fn get_used_link_args<'a>(cstore: &'a CStore) -> &'a [@str] { - let slice: &'a [@str] = cstore.used_link_args; - slice +pub fn get_used_link_args<'a>(cstore: &'a CStore) -> &'a [~str] { + cstore.used_link_args.as_slice() } pub fn add_extern_mod_stmt_cnum(cstore: &mut CStore, diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 48621459228..c43324456d1 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -1529,3 +1529,13 @@ pub fn get_trait_of_method(cdata: Cmd, id: ast::NodeId, tcx: ty::ctxt) } } + +pub fn get_native_libraries(cdata: Cmd) -> ~[~str] { + let libraries = reader::get_doc(reader::Doc(cdata.data), tag_native_libraries); + let mut result = ~[]; + do reader::tagged_docs(libraries, tag_native_libraries_lib) |lib_doc| { + result.push(lib_doc.as_str()); + true + }; + return result; +} diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 6da75397002..269054c6fd2 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -75,6 +75,7 @@ struct Stats { attr_bytes: u64, dep_bytes: u64, lang_item_bytes: u64, + native_lib_bytes: u64, impl_bytes: u64, misc_bytes: u64, item_bytes: u64, @@ -1633,6 +1634,23 @@ fn encode_lang_items(ecx: &EncodeContext, ebml_w: &mut writer::Encoder) { ebml_w.end_tag(); // tag_lang_items } +fn encode_native_libraries(ecx: &EncodeContext, ebml_w: &mut writer::Encoder) { + ebml_w.start_tag(tag_native_libraries); + + for &(ref lib, kind) in cstore::get_used_libraries(ecx.cstore).iter() { + match kind { + cstore::NativeStatic => {} // these libraries are not propagated + cstore::NativeUnknown => { + ebml_w.start_tag(tag_native_libraries_lib); + ebml_w.writer.write(lib.as_bytes()); + ebml_w.end_tag(); + } + } + } + + ebml_w.end_tag(); +} + struct ImplVisitor<'self> { ecx: &'self EncodeContext<'self>, ebml_w: &'self mut writer::Encoder, @@ -1750,6 +1768,7 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] { attr_bytes: 0, dep_bytes: 0, lang_item_bytes: 0, + native_lib_bytes: 0, impl_bytes: 0, misc_bytes: 0, item_bytes: 0, @@ -1806,6 +1825,11 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] { encode_lang_items(&ecx, &mut ebml_w); ecx.stats.lang_item_bytes = wr.tell() - i; + // Encode the native libraries used + i = wr.tell(); + encode_native_libraries(&ecx, &mut ebml_w); + ecx.stats.native_lib_bytes = wr.tell() - i; + // Encode the def IDs of impls, for coherence checking. i = wr.tell(); encode_impls(&ecx, crate, &mut ebml_w); @@ -1842,6 +1866,7 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] { println!(" attribute bytes: {}", ecx.stats.attr_bytes); println!(" dep bytes: {}", ecx.stats.dep_bytes); println!(" lang item bytes: {}", ecx.stats.lang_item_bytes); + println!(" native bytes: {}", ecx.stats.native_lib_bytes); println!(" impl bytes: {}", ecx.stats.impl_bytes); println!(" misc bytes: {}", ecx.stats.misc_bytes); println!(" item bytes: {}", ecx.stats.item_bytes); diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index ecd1c8985bd..1aff16cc23c 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -10,11 +10,12 @@ //! Finds crate binaries and loads their metadata - -use lib::llvm::{False, llvm, mk_object_file, mk_section_iter}; +use back::archive::Archive; +use driver::session::Session; +use lib::llvm::{False, llvm, ObjectFile, mk_section_iter}; use metadata::decoder; use metadata::encoder; -use metadata::filesearch::{FileSearch, FileMatch, FileMatches, FileDoesntMatch}; +use metadata::filesearch::{FileMatches, FileDoesntMatch}; use metadata::filesearch; use syntax::codemap::Span; use syntax::diagnostic::span_handler; @@ -26,6 +27,7 @@ use std::c_str::ToCStr; use std::cast; use std::io; +use std::libc; use std::num; use std::option; use std::os::consts::{macos, freebsd, linux, android, win32}; @@ -43,103 +45,176 @@ pub enum Os { } pub struct Context { - diag: @mut span_handler, - filesearch: @FileSearch, + sess: Session, span: Span, ident: @str, metas: ~[@ast::MetaItem], hash: @str, os: Os, - is_static: bool, intr: @ident_interner } -pub fn load_library_crate(cx: &Context) -> (~str, @~[u8]) { - match find_library_crate(cx) { - Some(t) => t, - None => { - cx.diag.span_fatal(cx.span, - format!("can't find crate for `{}`", - cx.ident)); - } +pub struct Library { + dylib: Option, + rlib: Option, + metadata: @~[u8], +} + +impl Context { + pub fn load_library_crate(&self) -> Library { + match self.find_library_crate() { + Some(t) => t, + None => { + self.sess.span_fatal(self.span, + format!("can't find crate for `{}`", + self.ident)); + } + } } -} -fn find_library_crate(cx: &Context) -> Option<(~str, @~[u8])> { - attr::require_unique_names(cx.diag, cx.metas); - find_library_crate_aux(cx, libname(cx), cx.filesearch) -} + fn find_library_crate(&self) -> Option { + attr::require_unique_names(self.sess.diagnostic(), self.metas); + let filesearch = self.sess.filesearch; + let crate_name = crate_name_from_metas(self.metas); + let (dyprefix, dysuffix) = self.dylibname(); -fn libname(cx: &Context) -> (~str, ~str) { - if cx.is_static { return (~"lib", ~".rlib"); } - let (dll_prefix, dll_suffix) = match cx.os { - OsWin32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX), - OsMacos => (macos::DLL_PREFIX, macos::DLL_SUFFIX), - OsLinux => (linux::DLL_PREFIX, linux::DLL_SUFFIX), - OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX), - OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX), - }; + // want: crate_name.dir_part() + prefix + crate_name.file_part + "-" + let dylib_prefix = format!("{}{}-", dyprefix, crate_name); + let rlib_prefix = format!("lib{}-", crate_name); - (dll_prefix.to_owned(), dll_suffix.to_owned()) -} + let mut matches = ~[]; + do filesearch::search(filesearch) |path| { + match path.filename_str() { + None => FileDoesntMatch, + Some(file) => { + let (candidate, existing) = if file.starts_with(rlib_prefix) && + file.ends_with(".rlib") { + debug!("{} is an rlib candidate", path.display()); + (true, self.add_existing_rlib(matches, path, file)) + } else if file.starts_with(dylib_prefix) && + file.ends_with(dysuffix) { + debug!("{} is a dylib candidate", path.display()); + (true, self.add_existing_dylib(matches, path, file)) + } else { + (false, false) + }; -fn find_library_crate_aux( - cx: &Context, - (prefix, suffix): (~str, ~str), - filesearch: @filesearch::FileSearch -) -> Option<(~str, @~[u8])> { - let crate_name = crate_name_from_metas(cx.metas); - // want: crate_name.dir_part() + prefix + crate_name.file_part + "-" - let prefix = format!("{}{}-", prefix, crate_name); - let mut matches = ~[]; - filesearch::search(filesearch, |path| -> FileMatch { - // FIXME (#9639): This needs to handle non-utf8 paths - let path_str = path.filename_str(); - match path_str { - None => FileDoesntMatch, - Some(path_str) => - if path_str.starts_with(prefix) && path_str.ends_with(suffix) { - debug!("{} is a candidate", path.display()); - match get_metadata_section(cx.os, path) { - Some(cvec) => - if !crate_matches(cvec, cx.metas, cx.hash) { - debug!("skipping {}, metadata doesn't match", - path.display()); - FileDoesntMatch - } else { - debug!("found {} with matching metadata", path.display()); - // FIXME (#9639): This needs to handle non-utf8 paths - matches.push((path.as_str().unwrap().to_owned(), cvec)); - FileMatches - }, - _ => { - debug!("could not load metadata for {}", path.display()); - FileDoesntMatch - } - } - } - else { - FileDoesntMatch - } - } - }); - - match matches.len() { - 0 => None, - 1 => Some(matches[0]), - _ => { - cx.diag.span_err( - cx.span, format!("multiple matching crates for `{}`", crate_name)); - cx.diag.handler().note("candidates:"); - for pair in matches.iter() { - let ident = pair.first(); - let data = pair.second(); - cx.diag.handler().note(format!("path: {}", ident)); - let attrs = decoder::get_crate_attributes(data); - note_linkage_attrs(cx.intr, cx.diag, attrs); + if candidate && existing { + FileMatches + } else if candidate { + match get_metadata_section(self.sess, self.os, path, + crate_name) { + Some(cvec) => + if crate_matches(cvec, self.metas, self.hash) { + debug!("found {} with matching metadata", + path.display()); + let (rlib, dylib) = if file.ends_with(".rlib") { + (Some(path.clone()), None) + } else { + (None, Some(path.clone())) + }; + matches.push(Library { + rlib: rlib, + dylib: dylib, + metadata: cvec, + }); + FileMatches + } else { + debug!("skipping {}, metadata doesn't match", + path.display()); + FileDoesntMatch + }, + _ => { + debug!("could not load metadata for {}", + path.display()); + FileDoesntMatch + } + } + } else { + FileDoesntMatch + } } - cx.diag.handler().abort_if_errors(); + } + } + + match matches.len() { + 0 => None, + 1 => Some(matches[0]), + _ => { + self.sess.span_err(self.span, + format!("multiple matching crates for `{}`", crate_name)); + self.sess.note("candidates:"); + for lib in matches.iter() { + match lib.dylib { + Some(ref p) => { + self.sess.note(format!("path: {}", p.display())); + } + None => {} + } + match lib.rlib { + Some(ref p) => { + self.sess.note(format!("path: {}", p.display())); + } + None => {} + } + let attrs = decoder::get_crate_attributes(lib.metadata); + note_linkage_attrs(self.intr, self.sess.diagnostic(), attrs); + } + self.sess.abort_if_errors(); None + } + } + } + + fn add_existing_rlib(&self, libs: &mut [Library], + path: &Path, file: &str) -> bool { + let (prefix, suffix) = self.dylibname(); + let file = file.slice_from(3); // chop off 'lib' + let file = file.slice_to(file.len() - 5); // chop off '.rlib' + let file = format!("{}{}{}", prefix, file, suffix); + + for lib in libs.mut_iter() { + match lib.dylib { + Some(ref p) if p.filename_str() == Some(file.as_slice()) => { + assert!(lib.rlib.is_none()); // XXX: legit compiler error + lib.rlib = Some(path.clone()); + return true; + } + Some(*) | None => {} + } + } + return false; + } + + fn add_existing_dylib(&self, libs: &mut [Library], + path: &Path, file: &str) -> bool { + let (prefix, suffix) = self.dylibname(); + let file = file.slice_from(prefix.len()); + let file = file.slice_to(file.len() - suffix.len()); + let file = format!("lib{}.rlib", file); + + for lib in libs.mut_iter() { + match lib.rlib { + Some(ref p) if p.filename_str() == Some(file.as_slice()) => { + assert!(lib.dylib.is_none()); // XXX: legit compiler error + lib.dylib = Some(path.clone()); + return true; + } + Some(*) | None => {} + } + } + return false; + } + + // Returns the corresponding (prefix, suffix) that files need to have for + // dynamic libraries + fn dylibname(&self) -> (&'static str, &'static str) { + match self.os { + OsWin32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX), + OsMacos => (macos::DLL_PREFIX, macos::DLL_SUFFIX), + OsLinux => (linux::DLL_PREFIX, linux::DLL_SUFFIX), + OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX), + OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX), } } } @@ -196,16 +271,26 @@ pub fn metadata_matches(extern_metas: &[@ast::MetaItem], local_metas.iter().all(|needed| attr::contains(extern_metas, *needed)) } -fn get_metadata_section(os: Os, - filename: &Path) -> Option<@~[u8]> { +fn get_metadata_section(sess: Session, os: Os, filename: &Path, + crate_name: &str) -> Option<@~[u8]> { unsafe { - let mb = filename.with_c_str(|buf| { - llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf) - }); - if mb as int == 0 { return option::None::<@~[u8]>; } - let of = match mk_object_file(mb) { - option::Some(of) => of, - _ => return option::None::<@~[u8]> + let mb = if filename.filename_str().unwrap().ends_with(".rlib") { + let archive = Archive::open(sess, filename.clone()); + let contents = archive.read(crate_name + ".o"); + let ptr = vec::raw::to_ptr(contents); + crate_name.with_c_str(|name| { + llvm::LLVMCreateMemoryBufferWithMemoryRangeCopy( + ptr as *i8, contents.len() as libc::size_t, name) + }) + } else { + filename.with_c_str(|buf| { + llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf) + }) + }; + if mb as int == 0 { return None } + let of = match ObjectFile::new(mb) { + Some(of) => of, + _ => return None }; let si = mk_section_iter(of.llof); while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False { @@ -266,11 +351,17 @@ pub fn read_meta_section_name(os: Os) -> &'static str { } // A diagnostic function for dumping crate metadata to an output stream -pub fn list_file_metadata(intr: @ident_interner, +pub fn list_file_metadata(sess: Session, + intr: @ident_interner, os: Os, path: &Path, out: @mut io::Writer) { - match get_metadata_section(os, path) { + // guess the crate name from the pathname + let crate_name = path.filename_str().unwrap(); + let crate_name = if crate_name.starts_with("lib") { + crate_name.slice_from(3) } else { crate_name }; + let crate_name = crate_name.split_iter('-').next().unwrap(); + match get_metadata_section(sess, os, path, crate_name) { option::Some(bytes) => decoder::list_crate_metadata(intr, bytes, out), option::None => { write!(out, "could not find metadata in {}.\n", path.display()) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index ac7b8240176..f14fc265b82 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -79,12 +79,12 @@ use syntax::ast_map::{path, path_elt_to_str, path_name, path_pretty_name}; use syntax::ast_util::{local_def}; use syntax::attr; -use syntax::attr::AttrMetaMethods; use syntax::codemap::Span; use syntax::parse::token; use syntax::parse::token::{special_idents}; use syntax::print::pprust::stmt_to_str; use syntax::{ast, ast_util, codemap, ast_map}; +use syntax::attr::AttrMetaMethods; use syntax::abi::{X86, X86_64, Arm, Mips, Rust, RustIntrinsic, OsWin32, OsAndroid}; use syntax::visit; use syntax::visit::Visitor; @@ -3106,18 +3106,6 @@ pub fn write_metadata(cx: &CrateContext, crate: &ast::Crate) { } } -// Writes the current ABI version into the crate. -pub fn write_abi_version(ccx: &mut CrateContext) { - unsafe { - let llval = C_uint(ccx, abi::abi_version); - let llglobal = "rust_abi_version".with_c_str(|buf| { - llvm::LLVMAddGlobal(ccx.llmod, val_ty(llval).to_ref(), buf) - }); - llvm::LLVMSetInitializer(llglobal, llval); - llvm::LLVMSetGlobalConstant(llglobal, True); - } -} - pub fn trans_crate(sess: session::Session, crate: ast::Crate, analysis: &CrateAnalysis, @@ -3177,7 +3165,6 @@ pub fn trans_crate(sess: session::Session, } glue::emit_tydescs(ccx); - write_abi_version(ccx); if ccx.sess.opts.debuginfo { debuginfo::finalize(ccx); } @@ -3217,10 +3204,18 @@ pub fn trans_crate(sess: session::Session, let llcx = ccx.llcx; let link_meta = ccx.link_meta; let llmod = ccx.llmod; + let crate_types = crate.attrs.iter().filter_map(|a| { + if "crate_type" == a.name() { + a.value_str() + } else { + None + } + }).map(|a| a.to_owned()).collect(); return CrateTranslation { context: llcx, module: llmod, - link: link_meta + link: link_meta, + crate_types: crate_types, }; } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index ac7c50fdfd8..3dcc1dbebca 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -67,7 +67,7 @@ struct buf { } // sundown FFI -#[link_args = "-lsundown"] +#[link(name = "sundown", kind = "static")] extern { fn sdhtml_renderer(callbacks: *sd_callbacks, options_ptr: *html_renderopt, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c51244cddf5..1a4537f9138 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -16,7 +16,8 @@ #[desc = "rustdoc, the Rust documentation extractor"]; #[license = "MIT/ASL2"]; -#[crate_type = "lib"]; +#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot +#[crate_type = "dylib"]; #[feature(globs, struct_variant, managed_boxes)]; diff --git a/src/librustpkg/lib.rs b/src/librustpkg/lib.rs index b87bd0c5824..157d8ba0105 100644 --- a/src/librustpkg/lib.rs +++ b/src/librustpkg/lib.rs @@ -17,7 +17,8 @@ url = "https://github.com/mozilla/rust/tree/master/src/librustpkg")]; #[license = "MIT/ASL2"]; -#[crate_type = "lib"]; +#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot +#[crate_type = "dylib"]; #[feature(globs, managed_boxes)]; @@ -114,7 +115,7 @@ fn parse<'a>(sysroot: Path, let options = @session::options { binary: binary, maybe_sysroot: Some(@sysroot), - crate_type: session::bin_crate, + outputs: ~[session::OutputExecutable], .. (*session::basic_options()).clone() }; let input = driver::file_input(script.clone()); diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index 5e867951b54..3b177b449d9 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -36,6 +36,7 @@ chmod_read_only, platform_library_name}; use rustc::back::link::get_cc_prog; use rustc::metadata::filesearch::rust_path; +use rustc::driver::session; use rustc::driver::driver::{build_session, build_session_options, host_triple, optgroups}; use syntax::diagnostic; use target::*; @@ -1829,10 +1830,11 @@ fn test_linker_build() { @diagnostic::Emitter); let test_sys = test_sysroot(); // FIXME (#9639): This needs to handle non-utf8 paths + let cc = get_cc_prog(sess); command_line_test([test_sys.as_str().unwrap().to_owned(), ~"install", ~"--linker", - get_cc_prog(sess), + cc, ~"foo"], workspace); assert_executable_exists(workspace, "foo"); diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index f21357d0271..a8dbf145e53 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -25,7 +25,6 @@ use syntax::util::small_vector::SmallVector; use rustc::back::link::output_type_exe; use rustc::back::link; -use rustc::driver::session::{lib_crate, bin_crate}; use context::{in_target, StopBefore, Link, Assemble, BuildContext}; use package_id::PkgId; use package_source::PkgSrc; @@ -195,8 +194,8 @@ pub fn compile_input(context: &BuildContext, debug!("compile_input's sysroot = {}", csysroot.display()); let crate_type = match what { - Lib => lib_crate, - Test | Bench | Main => bin_crate + Lib => session::OutputDylib, + Test | Bench | Main => session::OutputExecutable, }; let matches = getopts(debug_flags() + match what { @@ -239,7 +238,7 @@ pub fn compile_input(context: &BuildContext, debug!("Output type = {:?}", output_type); let options = @session::options { - crate_type: crate_type, + outputs: ~[crate_type], optimize: opt, test: what == Test || what == Bench, maybe_sysroot: Some(sysroot_to_use), diff --git a/src/librustuv/lib.rs b/src/librustuv/lib.rs index 76882e885a8..feb1c6b92bd 100644 --- a/src/librustuv/lib.rs +++ b/src/librustuv/lib.rs @@ -41,7 +41,9 @@ url = "https://github.com/mozilla/rust/tree/master/src/librustuv")]; #[license = "MIT/ASL2"]; -#[crate_type = "lib"]; +#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot +#[crate_type = "rlib"]; +#[crate_type = "dylib"]; #[feature(macro_rules, globs)]; diff --git a/src/librustuv/uvll.rs b/src/librustuv/uvll.rs index 3917efe9ca8..f62ac3450e6 100644 --- a/src/librustuv/uvll.rs +++ b/src/librustuv/uvll.rs @@ -524,8 +524,13 @@ pub unsafe fn guess_handle(handle: c_int) -> c_int { // second copies of everything. We obviously don't want this, so instead of // dying horribly during testing, we allow all of the test rustuv's references // to get resolved to the original rustuv crate. -#[link_args = "-luv_support -luv"] -#[cfg(not(test))] +#[cfg(not(test), not(stage0))] +#[link(name = "uv_support", kind = "static")] +#[link(name = "uv", kind = "static")] +extern {} + +#[cfg(not(test), stage0)] +#[link_args = "-luv -luv_support"] extern {} extern { @@ -717,12 +722,26 @@ pub fn uv_signal_start(h: *uv_signal_t, cb: uv_signal_cb, pub fn uv_signal_stop(handle: *uv_signal_t) -> c_int; } -// libuv requires various system libraries to successfully link on some -// platforms -#[cfg(target_os = "linux")] +// various platform libraries required by libuv +#[cfg(not(stage0))] +#[link(name = "pthread")] +extern {} +#[cfg(stage0)] #[link_args = "-lpthread"] extern {} -#[cfg(target_os = "win32")] +#[cfg(target_os = "win32", not(stage0))] +#[link(name = "ws2_32")] +#[link(name = "psapi")] +#[link(name = "iphlpapi")] +extern {} +#[cfg(target_os = "win32", stage0)] #[link_args = "-lws2_32 -lpsapi -liphlpapi"] extern {} + +#[cfg(target_os = "freebsd", not(stage0))] +#[link(name = "kvm")] +extern {} +#[cfg(target_os = "freebsd", stage0)] +#[link_args = "-lkvm"] +extern {} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index a72bc6b8328..0489fff31f9 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -51,7 +51,9 @@ #[comment = "The Rust standard library"]; #[license = "MIT/ASL2"]; -#[crate_type = "lib"]; +#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot +#[crate_type = "rlib"]; +#[crate_type = "dylib"]; #[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk.png", html_favicon_url = "http://www.rust-lang.org/favicon.ico", @@ -79,15 +81,7 @@ #[cfg(test)] pub use ops = realstd::ops; #[cfg(test)] pub use cmp = realstd::cmp; -// On Linux, link to the runtime with -lrt. -#[cfg(target_os = "linux")] -#[doc(hidden)] -pub mod linkhack { - #[link_args="-lrustrt -lrt"] - #[link_args = "-lpthread"] - extern { - } -} +mod rtdeps; /* The Prelude. */ diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs new file mode 100644 index 00000000000..761d630f771 --- /dev/null +++ b/src/libstd/rtdeps.rs @@ -0,0 +1,48 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This module contains the linkage attributes to all runtime dependencies of +//! the stndard library This varies per-platform, but these libraries are +//! necessary for running libstd. + +// All platforms need to link to rustrt +#[link(name = "rustrt")] +extern {} + +// LLVM implements the `frem` instruction as a call to `fmod`, which lives in +// libm. Hence, we must explicitly link to it. +// +// On linux librt and libdl are indirect dependencies via rustrt, +// and binutils 2.22+ won't add them automatically +#[cfg(target_os = "linux")] +#[link(name = "rt")] +#[link(name = "dl")] +#[link(name = "m")] +#[link(name = "pthread")] +extern {} + +#[cfg(target_os = "android")] +#[link(name = "dl")] +#[link(name = "log")] +#[link(name = "supc++")] +#[link(name = "gnustl_shared")] +#[link(name = "m")] +extern {} + +#[cfg(target_os = "freebsd")] +#[link(name = "execinfo")] +#[link(name = "rt")] +#[link(name = "stdc++")] +#[link(name = "pthread")] +extern {} + +#[cfg(target_os = "macos")] +#[link(name = "pthread")] +extern {} diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 351eab35a77..7fd503a8d42 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -19,7 +19,8 @@ uuid = "9311401b-d6ea-4cd9-a1d9-61f89499c645")]; #[license = "MIT/ASL2"]; -#[crate_type = "lib"]; +#[crate_type = "lib"]; // NOTE: remove after stage0 snapshot +#[crate_type = "dylib"]; #[feature(macro_rules, globs, managed_boxes)]; diff --git a/src/rustllvm/rustllvm.def.in b/src/rustllvm/rustllvm.def.in index a6b887f058a..069f29e909d 100644 --- a/src/rustllvm/rustllvm.def.in +++ b/src/rustllvm/rustllvm.def.in @@ -630,3 +630,5 @@ LLVMAddReturnAttribute LLVMRemoveReturnAttribute LLVMTypeToString LLVMAddColdAttribute +LLVMCreateMemoryBufferWithMemoryRange +LLVMCreateMemoryBufferWithMemoryRangeCopy