rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.91k stars 12.52k forks source link

Add rustc --emit=link-info for staticlib link-line output #31471

Open rillian opened 8 years ago

rillian commented 8 years ago

When linking rust into C++ projects, I use rustc --crate-type=staticlib to generate a static library which I can link into the overall project. Since there's no standard for transitive dependency declaration in the C ABI for static libraries, rustc prints out a list of libraries which need to be linked along with the staticlib. (https://github.com/rust-lang/rust/issues/25820#issuecomment-106480235)

This is very helpful in development, but not ideal for automation. I propose adding an --emit=link-info option (--emit=libs? --emit=ldflags?) to write the required link line out to a file for use later, similar to --emit=dep-info for makefile dependency generation.

Order of evaluation is a little trickier than with dep-info since it has to work the first time, but this should simplify funky stderr hacks like

libfoo.a libfoo.a.out: foo/lib.rs
    rustc -g --crate-type staticlib --crate-name foo \
      --emit dep-info,link=$@ $< \
      2> $@.out || cat $@.out >&2
-include foo.d

prog: RUST_LIBS = $(shell awk '/^note: library: / {print "-l"$$3}' libmp4parse.a.out)
prog: prog.o libfoo.a
    $(CXX) $(CXXFLAGS) -o $@ $^ $(RUST_LIBS)

into

libfoo.a libfoo.a.link: foo/lib.rs
    rustc -g --crate-type staticlib --crate-name foo \
      --emit dep-info,link=$@,link-info=$@.link $<
-include foo.d

prog: prog.o libfoo.a
    $(CXX) $(CXXFLAGS) -o $@ $^ $(shell cat libfoo.a.link)
alexcrichton commented 8 years ago

This seems reasonable to me, although I might prefer to call it something like --emit link-flags-ld or something where we can swap out link.exe, lld, ld, or all the other random linkers in there as well. That way we can hopefully pick the "right format" for the linker arguments.

I think we could even suppress the "warning, you gotta link these libs" message when you're emitting these as well. We may even be able to get crazy and put other flags in there (e.g. things like --gc-sections or something)

retep998 commented 8 years ago

Since there's no standard for transitive dependency declaration in the C ABI for static libraries

While there might not be a standard for most compilers, msvc definitely does have a way for libraries to specify dependencies that are pulled in automatically.

rillian commented 8 years ago

Oh! That would be great. Can you share some documentation about how this works? As far as I knew msvc .lib static libraries were just ar archives of COFF object files--with no link metadata--the same as Linux and Mac.

retep998 commented 8 years ago

I can't find any documentation on it at the moment, just some other documentation that alludes to it.

https://msdn.microsoft.com/en-us/library/f1tbxcxh.aspx

By default, the compiler puts the name of the library into the .obj file to direct the linker to the correct library.

https://msdn.microsoft.com/en-us/library/229a6ysd.aspx

before default libraries named in .obj files.

So the capability is there in object files, I just have no idea how to add that information to the object file.

rillian commented 8 years ago

Thanks for the links. I still don't see a way to make this work though. /DEFAULTLIB is an option to LINK which make executables and dlls, and isn't accepted by LIB, the command for creating static libraries.

I think the /Zl option is about the run-time library. Maybe it's possible to abuse that for transitive dependencies in object files if it's possible to have more than one? I set up a simple test project, but couldn't make it work. https://github.com/rillian/msvc-staticlib Let me know if you have better luck, @retep998.

retep998 commented 8 years ago

Really it's just a matter of seeing whether LLVM can do it, since LLVM is what creates our object files, and is also what creates static libraries for us (Rust doesn't use LIB).

BartMassey commented 8 years ago

As noted in #33173 just writing the libraries note to stdout instead of stderr would help a lot. Heck, create a .libs-needed file and write to that.

Better yet, can someone finish @rillian's #31875, or can we just merge it and add the lack of tests and docs as a separate issue? I feel like waiting for better tests and docs would be nice, but this is kind of an obnoxious situation and it looks like @rillian has dropped out of trying to get a fix in.

I'm currently working around this in my Makefile (because reasons) with

    rustc staticlib.rs 2>&1 | egrep -v '^note:' | cat

where the cat is to suppress the stupid exit status 1 from egrep when its output is empty. Uggh.

rillian commented 8 years ago

Yeah, I haven't been able to work more on this. Would be very happy to have someone finish it up!

retep998 commented 7 years ago

Ah, I figured out how to do the default lib stuff in LLVM, based on what clang does. This method lets you pass arbitrary arguments to the linker without having to explicitly pass them to the linker.

!0 = !{i32 6, !"Linker Options", !1}
!1 = !{!2, !3, !4, !5, !6, !7, !8}
!2 = !{!"/DEFAULTLIB:libcmt.lib"}
!3 = !{!"/DEFAULTLIB:oldnames.lib"}
!4 = !{!"/FAILIFMISMATCH:\22_MSC_VER=1900\22"}
!5 = !{!"/FAILIFMISMATCH:\22_ITERATOR_DEBUG_LEVEL=0\22"}
!6 = !{!"/FAILIFMISMATCH:\22RuntimeLibrary=MT_StaticRelease\22"}
!7 = !{!"/DEFAULTLIB:libcpmt.lib"}
!8 = !{!"/FAILIFMISMATCH:\22_CRT_STDIO_ISO_WIDE_SPECIFIERS=0\22"}