Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce static_to_shared_library function #6092

Merged
merged 1 commit into from
Nov 7, 2017

Conversation

michaelkuhn
Copy link
Member

This is work in progress based on the discussion in #6008.

@junghans: Sorry for the timing. I was just working on this when you merged #6008. :-)

The static_to_shared_library function takes an existing static library and produces a shared library based on it.

For now, I just put it into the lua package. I would like to get some feedback whether the function's interface etc. are what you had in mind. Additionally, I would need some hints on how to integrate this better into Spack. (I have already experimented with putting it into build_environment.py but I currently need access to spack_cc and dso_suffix.)

@junghans
Copy link
Contributor

junghans commented Nov 2, 2017

@tgamblin I would really like to see static_to_shared_library as a part of the core, where would we put this?

@michaelkuhn
Copy link
Member Author

Small update: I moved it into the package class, which has the benefit of giving us access to the spec.

@junghans
Copy link
Contributor

junghans commented Nov 2, 2017

Copy link
Member

@tgamblin tgamblin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, several questions/thoughts:

  1. How well does this work across different compilers? Are the arguments portable?
  2. Does this require that the static lib already be PIC? Or does it work in general?
  3. This seems like something that should only exist in the build environment (though if there are other uses maybe we should rethink that). Maybe the right place for it is in build_environment.py, and it could be a function added to the module scope in, e.g., set_module_variables_for_package(). You have the Package and spack_cc there, so you could construct a converter function specialized for the build.
  4. Needs a test.

@michaelkuhn
Copy link
Member Author

I have refactored the code a bit, put it into build_environment.py and added a rough first test for it (see below).

How well does this work across different compilers? Are the arguments portable?

From what I could find, at least GCC, Intel and Clang should support -Wl. GCC and Clang support -shared, -dynamiclib and -install_name. The rest depends on the linker.

Does this require that the static lib already be PIC? Or does it work in general?

The static library has to be compiled with PIC.

This seems like something that should only exist in the build environment (though if there are other uses maybe we should rethink that). Maybe the right place for it is in build_environment.py, and it could be a function added to the module scope in, e.g., set_module_variables_for_package(). You have the Package and spack_cc there, so you could construct a converter function specialized for the build.

Done. I moved the compiler selection out of the function to be able to test it more easily.

Needs a test.

I added a rough first test for it that can be extended later. Please check whether this is what you had in mind.

@mathstuf
Copy link
Contributor

mathstuf commented Nov 2, 2017

Open questions:

  • What to do for Windows? Symbols need to be exported there, so a .def file listing the symbols to export from the shared library is required. Headers may also need patched to set the proper dllimport settings (static libraries tend to have no declspec at all). Blindly listing the symbols for a .def file may end up exporting supposed-to-be-internal symbols as well.
  • How to handle multiple static libraries in a package? What if they have circular dependencies?
  • Static builds may have compiled out some features necessary in a shared build (though it seems this is more for coercing never-shared projects to be shared rather than replacing an intentional link=shared option).
  • Are soname numbers just skipped? What about library IDs on macOS? I see something is set; is that a full path or just the filename?
  • Other compiler support?

@michaelkuhn
Copy link
Member Author

What to do for Windows? Symbols need to be exported there, so a .def file listing the symbols to export from the shared library is required. Headers may also need patched to set the proper dllimport settings (static libraries tend to have no declspec at all). Blindly listing the symbols for a .def file may end up exporting supposed-to-be-internal symbols as well.

No idea, to be honest. Someone else will have to take care of this, as I do not have access to a Windows build environment.

How to handle multiple static libraries in a package? What if they have circular dependencies?
Static builds may have compiled out some features necessary in a shared build (though it seems this is more for coercing never-shared projects to be shared rather than replacing an intentional link=shared option).

This is just meant as an easy way to convert existing static libraries to shared ones, in case the package in question does not build them (see lua). Instead of patching the package, we can just use this function.

Are soname numbers just skipped? What about library IDs on macOS? I see something is set; is that a full path or just the filename?

Sorry, I skipped them for the initial version. I just pushed an updated version with support for specifying a version.

Other compiler support?

I think it should be pretty easy to extend the function to support additional compilers.

Copy link
Member

@tgamblin tgamblin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me -- I think we can punt on windows for the moment, but there are still a few minor requests.

os.environ['SPACK_TEST_COMMAND'] = 'dump-args'

expected = {
'linux': '/bin/mycc -Wl,-rpath,/spack-test-prefix/lib -Wl,-rpath,/spack-test-prefix/lib64 -shared -Wl,-soname,{2} -Wl,--whole-archive {0} -Wl,--no-whole-archive -o {1}', # noqa: E501
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split across multiple lines (no NOQA)


class BuildEnvironmentTest(unittest.TestCase):

def setUp(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use a pytest fixture for this? We still have some old unittest style tests but we're trying to switch to pytest. You can probably just make a build_environment fixture that does all the same stuff as this, and yields a named tuple with cc, ld, etc. It should probably also unset the environment variables after yielding so as not to interfere with other tests.

if 'SPACK_DEPENDENCIES' in os.environ:
del os.environ['SPACK_DEPENDENCIES']

def test_static_to_shared_library(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is fine but can be moved to module scope if it's a pytest test. It should take the build_environment fixture as a parameter.

soname += '.{0}'.format(version)

compiler_args = [
'-shared',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a TODO in here somewhere that says these arguments should really be added to the various compiler classes and generalized for different compilers?

Converts a static library to a shared library.

Parameters:
static_lib (str): Path to the static library.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a note here or in the description that this library must be built with PIC.

@michaelkuhn michaelkuhn force-pushed the static-to-shared branch 2 times, most recently from 20c7fb2 to 2786557 Compare November 2, 2017 21:53
@michaelkuhn
Copy link
Member Author

I have pushed a new version. It includes some updates regarding the version handling (if we set a version X.Y.Z, the library is expected to be found at libfoo.so.X.Y.Z, so we create a few symlinks) and should address all of @tgamblin's comments.

@tgamblin
Copy link
Member

tgamblin commented Nov 2, 2017

Thanks! LGTM!

@michaelkuhn
Copy link
Member Author

I wonder why the CI tests are failing, they work fine locally. Is there something special about the CI environment?

# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://github.com/llnl/spack
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update url to spack/spack!

The static_to_shared_library function takes an existing static library
and produces a shared library based on it.
@michaelkuhn
Copy link
Member Author

OK, I think I have found the problem (SPACK_DEBUG_LOG_ID was missing). Should be good to go now. :-)

@junghans junghans merged commit ca73103 into spack:develop Nov 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants