GenericMappingTools / gmt

The Generic Mapping Tools
https://www.generic-mapping-tools.org
Other
850 stars 356 forks source link

Static build doesn't work #2436

Closed seisman closed 4 years ago

seisman commented 4 years ago

Tried to have a static build by setting BUILD_SHARED_LIBS to FALSE. The build is succesful, and gmt can print the splash screen, but gmt coast says No module named coast was found.

PaulWessel commented 4 years ago

tool -L gmt says what?

seisman commented 4 years ago
$ otool -L gmt
gmt:
    /usr/local/opt/netcdf/lib/libnetcdf.15.dylib (compatibility version 15.0.0, current version 15.0.0)
    /usr/lib/libcurl.4.dylib (compatibility version 7.0.0, current version 9.0.0)
    /usr/local/opt/gdal/lib/libgdal.20.dylib (compatibility version 26.0.0, current version 26.2.0)
    @rpath/libpcre.1.dylib (compatibility version 4.0.0, current version 4.11.0)
    /usr/local/opt/fftw/lib/libfftw3f.3.dylib (compatibility version 9.0.0, current version 9.8.0)
    /usr/local/opt/fftw/lib/libfftw3f_threads.3.dylib (compatibility version 9.0.0, current version 9.8.0)
    /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate (compatibility version 1.0.0, current version 4.0.0)
    /usr/local/opt/glib/lib/libglib-2.0.0.dylib (compatibility version 6201.0.0, current version 6201.4.0)
    /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)

The file size of the gmt executable is ~4.6 Mb.

PaulWessel commented 4 years ago

So is there static libraries being built. All that stuff in libgmt should now be in gmt.exe, no? But that cannot be the case. Are there any libgmt* files being produced?

seisman commented 4 years ago

I think the static libraries are already included in the gmt executable. That's why the gmt.exe is as large as 4.6 Mb.

The static libraries are still being built in the lib directory. Below are what I have in my lib directory

.
├── gmt
│   └── plugins
│       └── supplements.so
├── libgmt.6.1.0.dylib
├── libgmt.6.dylib -> libgmt.6.1.0.dylib
├── libgmt.a
├── libgmt.dylib -> libgmt.6.dylib
├── libpostscriptlight.6.1.0.dylib
├── libpostscriptlight.6.dylib -> libpostscriptlight.6.1.0.dylib
├── libpostscriptlight.a
└── libpostscriptlight.dylib -> libpostscriptlight.6.dylib
PaulWessel commented 4 years ago

Does nm -gU gmt list all the expected symbols that normally would be listed by

nm -gU libgmt.dylib  | grep GMT_grdimage
000000000045db90 T _GMT_grdimage
seisman commented 4 years ago

Sorry, I mixed the libraries from the static and shared builds. For static build, I have

.
├── gmt
│   └── plugins
│       └── supplements.so
├── libgmt.a
└── libpostscriptlight.a
seisman commented 4 years ago

Does nm -gU gmt list all the expected symbols that normally would be listed by

nm -gU libgmt.dylib  | grep GMT_grdimage
000000000045db90 T _GMT_grdimage

Yes.

gmt coast -Vd gives:

gmt coast -Vd                                                                                                                                                                                      78 ↵
gmt [DEBUG]: Obtained the ppid from parent: 56674
gmt [DEBUG]: Enter: gmtinit_new_GMT_ctrl
gmt [DEBUG]: GMT->session.SHAREDIR = /Users/seisman/local/GMT/share
gmt [DEBUG]: GMT->session.HOMEDIR = /Users/seisman
gmt [DEBUG]: GMT->session.USERDIR = /Users/seisman/.gmt [created]
gmt [DEBUG]: GMT->session.CACHEDIR = /Users/seisman/.gmt/cache [created]
gmt [DEBUG]: GMT: 0. Will try to find subdir=postscriptlight stem = PSL_custom_fonts suffix=.txt
gmt [DEBUG]: GMT: 1. gmt_getsharepath trying current dir
gmt [DEBUG]: GMT: 2. gmt_getsharepath trying USERDIR /Users/seisman/.gmt
gmt [DEBUG]: Map distance calculation will be Cartesian
gmt [DEBUG]: Exit:  gmtinit_new_GMT_ctrl
gmt [DEBUG]: Enter: New_PSL_Ctrl
gmt [DEBUG]: Exit:  New_PSL_Ctrl
gmt [DEBUG]: Enter: gmt_manage_workflow
gmt [DEBUG]: Exit : gmt_manage_workflow
gmt [DEBUG]: Enter: PSL_beginsession
gmt [DEBUG]: Exit : PSL_beginsession
gmt [DEBUG]: Enter: PSL_setdefaults
gmt [DEBUG]: Exit : PSL_setdefaults
gmt [DEBUG]: Enter: gmtlib_io_init
gmt [DEBUG]: Exit : gmtlib_io_init
gmt [DEBUG]: Enter: gmt_hash_init
gmt [DEBUG]: Exit:  gmt_hash_init
gmt [DEBUG]: Enter: gmt_hash_init
gmt [DEBUG]: Exit:  gmt_hash_init
gmt [DEBUG]: The PROJ_GEODESIC set to Vincenty
gmt [DEBUG]: Enter: gmt_getdefaults
gmt [DEBUG]: Look for file /Users/seisman/gmt.conf
gmt [DEBUG]: Look for file /Users/seisman/.gmt/gmt.conf
gmt [DEBUG]: Look for file /Users/seisman/.gmt/server/gmt.conf
gmt [DEBUG]: Look for file /Users/seisman/.gmt/cache/gmt.conf
gmt [DEBUG]: Could not find file gmt.conf
gmt [DEBUG]: Exit:  gmt_getdefaults
gmt [DEBUG]: Enter: gmtlib_plot_C_format
gmt [DEBUG]: Exit:  gmtlib_plot_C_format
gmt [DEBUG]: Enter: gmt_get_history
gmt [DEBUG]: Initialize FFTW with 8 threads.
gmt [DEBUG]: GMT_Create_Session initialized GMT structure
gmt [DEBUG]: Shared Library # 0 (core). Path = libgmt.a
gmt [DEBUG]: Loading GMT plugins from: /Users/seisman/local/GMT/bin/gmt/plugins

ERROR: No module named coast was found.  This could mean one of four cases:
  1. There actually is no such module; please check your spelling.
  2. You used a modern mode module name while running in GMT classic mode.
  3. Module exists in the GMT supplemental library, but the library could not be found.
  4. Module exists in a GMT custom library, but none was specified via GMT_CUSTOM_LIBS.
Shared libraries must be in standard system paths or set via environmental parameter DYLD_LIBRARY_PATH.

gmt [DEBUG]: Entering GMT_Destroy_Session
PaulWessel commented 4 years ago

Our code for getting the pointer to the selected module assumes shared library I think. Perhaps the stuff in gmt_api.c (_api_initsharedlibs) and loop over shared libs in _GMT_CallModule is just wrong for static. If static, don't we just call that function directly? I.e., instead of things like

    for (lib = 0; lib < API->n_shared_libs; lib++) {    /* Look for gmt_module in any of the shared libs */
        *(void **) (&p_func) = api_get_module_func (API, gmt_module, lib);
        if (p_func) break;  /* Found it in this shared library */
    }

we simply have

    `*(void **) (&p_func) = gmt_core_module_lookup (API, gmt_module);`

and then

    `status = (*p_func) (V_API, mode, args);                /* Call the module in peace */`

i.e., no checking.

seisman commented 4 years ago

It sounds correct.

PaulWessel commented 4 years ago

Shall I have a look at this or have you already started? I think I can get this working pretty quickly if I can find what #defines are used for the static vs shared.

seisman commented 4 years ago

I know little about shared libraries, so you may need to take a look at it.

PaulWessel commented 4 years ago

OK, will have a look.

PaulWessel commented 4 years ago

About done - the gmt_make_module_src.sh needed some updates. Running tests now.

PaulWessel commented 4 years ago

Progress. Got all to run but accessing GMT parameters from supplemental functions yields trouble and failed tests:

    265 - geodesy/test_earthtide.sh (Failed)
    450 - img/imgmap.sh (Failed)
    451 - img/imgtrack.sh (Failed)
    805 - spotter/spotter_06.sh (Failed)
    834 - x2sys/x2sys_05.sh (Failed)

The log has stuff like

earthtide [ERROR]: Syntax error: Unrecognized keyword FORMAT_FLOAT_OUT.

Don't know what that means so need to debug.

PaulWessel commented 4 years ago

Returning to this with the plan to debug but now I am unable to build. Perhaps something changed with Xcode or with our CMake system? It is failing at this point now:

[219/248] Linking C static library src/libpostscriptlight.a
FAILED: src/libpostscriptlight.a 
: && /opt/local/bin/cmake -E remove src/libpostscriptlight.a && /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ar qc src/libpostscriptlight.a  src/CMakeFiles/pslib.dir/postscriptlight.c.o && /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib src/libpostscriptlight.a && /opt/local/bin/cmake -E touch src/libpostscriptlight.a && cd /Users/pwessel/UH/RESEARCH/CVSPROJECTS/GMTdev/gmt-dev/dbuild/src && /usr/bin/dsymutil /Users/pwessel/UH/RESEARCH/CVSPROJECTS/GMTdev/gmt-dev/dbuild/src/libpostscriptlight.a
error: cannot parse the debug map for '/Users/pwessel/UH/RESEARCH/CVSPROJECTS/GMTdev/gmt-dev/dbuild/src/libpostscriptlight.a': The file was not recognized as a valid object file
[225/248] Building C object src/CMakeFiles/testgmt.dir/testgmt.c.o
ninja: build stopped: subcommand failed.

Previously it built just fine and we were having some issued with external variables I think.

seisman commented 4 years ago

The same happens to me, but I don't think we made any changes to cmake recently, except #2667, which is unmerged.

PaulWessel commented 4 years ago

I switched to Xcode 10.3 and got the same. Wonder if there is some setting in the ConfigUser that I am missing relative to when I did this 26 days ago...

seisman commented 4 years ago

I don't know what happened, but now I can do the static builds again. @PaulWessel Please try again.

PaulWessel commented 4 years ago

Strange, I still get (this time at work - yesterday was at home):

error: cannot parse the debug map for '/Users/pwessel/GMTdev/gmt-dev/dbuild/src/libpostscriptlight.a': The file was not recognized as a valid object file

PaulWessel commented 4 years ago

Googling indicates it has to do with debug information being stripped off before this command runs. Based on that information it was suggested to add these:

set_target_properties(${TARGETNAME} PROPERTIES XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym")
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")

but no change. I even hardwired

set (CMAKE_BUILD_TYPE Debug)

PaulWessel commented 4 years ago

Breaking things down a bit more; this is the failing command that complains:

/usr/bin/dsymutil /Users/pwessel/GMTdev/gmt-dev/dbuild/src/libpostscriptlight.a

PaulWessel commented 4 years ago

Some progress. Building with Release worked but not RelWithDebInfo. So anything with Debug seems to fail at dsymutil step. However, building purely for Xcode to debug in Xcode worked so I will see if I can solve the problem with the extern array. I suspect if you try to build with gcc then it will also work.

PaulWessel commented 4 years ago

Debug in Xcode shows that the static array _keyshashnode declared and filled in gmt_init.c during gmt_begin is empty when earthtide (a module in the shared dynamic library supplements.so) calls a function in the static library that needs to look at _keyshashnode. The array is correctly filled during gmt_begin but all is lost by the round-trip from calling the shared library function that calls a static function.

I made changes such as removing the static and adding EXTERN_MSC when building statically but no luck. I suspect the static and shared libraries have separate data spaces and their own copies of this variable, and only the static one is updated. But not sure.

PaulWessel commented 4 years ago

I have made a branch static-build-fix for experimenting with this issue.

PaulWessel commented 4 years ago

Maybe gurus like @remkos or @ldldr might now how this should work when we mix libgmt.a with supplemental.so and we actually have some global (now extern) arrays created in libgmt.a but requested by a module in supplemental.so that then calls functions in libgmt.a that need those arrays [they are empty right now].

PaulWessel commented 4 years ago

I suspect we need to add the _keyshashnode structure array to the GMT allocated struct for this to work for both static and shared builds.

PaulWessel commented 4 years ago

Closing this as we wont support static for now.

TidbitSoftware commented 1 year ago

Hi, we're encountering a similar issue after upgrading to 6.4.0. Now, when we call, for example, gmt select, we get the same error about the module not being found. We rely on the static build in order to distribute our software with as few system lib dependencies as possible. Has there been any progress on this since this issue was closed? Thanks!

Edit: It appears that the gmt executable is still attempting to load libgmt.dylib (which doesn't/shouldn't exist):

gmt [DEBUG]: Loading core GMT shared library: libgmt.dylib
gmt [DEBUG]: Shared Library # 0 (core). Path = libgmt.dylib
PaulWessel commented 1 year ago

If I naively assume that creating a static build should really only involve setting certain linker flags or CMAKE settings. If anyone is interesting in pursuing that we would of course be happy to merge in a PR that would do this. But I suspect none of the volunteer developers have any particular interest in static builds. If TidBit is interesting in solving this and make a PR then that would be great since it sounds like you are motivated.

joa-quim commented 1 year ago

Actually is more complicated than that. The largest number of GMT dependencies comes through GDAL where there have been so questions on static libs and the conclusion is no way, we have a lot of dependencies and woul'd need to all be build statically.

TidbitSoftware commented 1 year ago

For what it's worth, we do compile and link GDAL statically for our distributables.

joa-quim commented 1 year ago

I guess that means your GDAL lib has a reduced number of dependencies.

https://bugs.launchpad.net/ubuntu/+source/gdal/+bug/1956840

TidbitSoftware commented 1 year ago

@PaulWessel I would be happy to work on this given time. Just to get the high level conversation started, I suspect you would want a new CMake config variable to control all of this, like, LINK_EXECUTABLES_TO_STATIC_LIBS?

TidbitSoftware commented 1 year ago

@joa-quim We are currently compiling version 3.5.3 from source, and also compile zlib, hdf5, and netcdf statically from source. It does work, but it sounds like it is not a common use case. I understand that the modern paradigm is to link against shared libraries and ship those with your distributable, but because we run our software in MATLAB (in particular) we were experiencing a lot of shared library conflicts and it was easier to reduce variables by compiling and linking static external package libraries.

joa-quim commented 1 year ago

I have to leave now but I'm curious about the netCDF build. I couldn't build it on Windows (have an open issue in the netCDF repo). Well I built it but with lots of trickery involving hacking-and-reusing the vcpkg ninja build file.

And yes, we are very well aware of the Matlab issues in other OS than Windows.

TidbitSoftware commented 1 year ago

@joa-quim Yes, our Windows build is still under development and has a reduced set of external packages and capabilities. In particular, we have not yet incorporated netCDF into this build (as we only use it as a prerequisite for GDAL). But I feel your pain: we used MSYS2 MinGW to be able to reuse a lot of the existing configuration from our *nix builds and it was not always straightforward.

TidbitSoftware commented 1 year ago

@PaulWessel So a big hint here is that when I revert to version 6.0.0 in our install script but change nothing else about our configuration, gmtselect does work. The 6.0.0 docs indicated that the gmt module syntax was already available at that time, though I believe it was not yet the default to only compile the gmt executable. Are there changelogs available (I couldn't easily find them on the website)? Between that and parsing repo diffs it might be relatively trivial for myself or someone else to ensure that the gmt executable and others are linked statically to libgmt in more recent versions the way that they were in 6.0.0.

PaulWessel commented 1 year ago

@PaulWessel I would be happy to work on this given time. Just to get the high level conversation started, I suspect you would want a new CMake config variable to control all of this, like, LINK_EXECUTABLES_TO_STATIC_LIBS?

Yes, something that would be FALSE by default.

PaulWessel commented 1 year ago

We have GMT_INSTALL_MODULE_LINKS which controls whether we build links called gmtselect, grdimage etc but otherwise we expect people to do thegmt module option path due to namespace solution issues.

TidbitSoftware commented 1 year ago

We have GMT_INSTALL_MODULE_LINKS which controls whether we build links called gmtselect, grdimage etc but otherwise we expect people to do the gmt module option path due to namespace solution issues.

Yes, I tried rebuilding with this first, but those links seem to just be sort of aliases (i.e. gmtselect calls gmt with arg select)? Because the base gmt was still linked against libgmt.dylib (which doesn't exist), this still fails.

PaulWessel commented 1 year ago

OK, expected that but thought I mention it.

joa-quim commented 1 year ago

But I feel your pain: we used MSYS2 MinGW to be able to reuse a lot of the existing configuration from our *nix builds and it was not always straightforward.

Actually, except now for netCDF (their fault), it's quite some work because I build all dependencies with custom names, and hence escape the Matlab shits that way, but all runs smoothly with VisualStudio. Out GDAL build has:

┌ Info: GDAL 3.7.0dev-2cefafcbad-dirty, released 2023/01/05
│ 210 GDAL drivers found
└ 77 OGR drivers found

I can't build GMT with MinGW due to a strange error at the linking stage when it tries to link with libcurl. Did you manage to build it with MinGW?

TidbitSoftware commented 1 year ago

Did you manage to build it with MinGW?

No, that's on the back burner for me, but I'll be happy to share with you if/when I do accomplish it.