Linking static libraries to other static libraries

2019-01-01 12:48发布

问题:

I have a small piece of code that depends on many static libraries (a_1-a_n). I\'d like to package up that code in a static library and make it available to other people.

My static library, lets call it X, compiles fine.

I\'ve created a simple sample program that uses a function from X, but when I try to link it to X, I get many errors about missing symbols from libraries a_1 - a_n.

Is there a way that I can create a new static library, Y that contains X and all the functionality needed by X (selected bits from a_1 - a_n), so that I can distribute just Y for people to link their programs to?


UPDATE:

I\'ve looked at just dumping everything with ar and making one mega-lib, however, that ends up including a lot of symbols that are not needed (all the .o files are about 700 MB, however, a statically linked executable is 7 MB). Is there a nice way to include only what is actually needed?


This looks closely related to How to combine several C/C++ libraries into one?.

回答1:

Static libraries do not link with other static libraries. The only way to do this is to use your librarian/archiver tool (for example ar on Linux) to create a single new static library by concatenating the multiple libraries.

Edit: In response to your update, the only way I know to select only the symbols that are required is to manually create the library from the subset of the .o files that contain them. This is difficult, time consuming and error prone. I\'m not aware of any tools to help do this (not to say they don\'t exist), but it would make quite an interesting project to produce one.



回答2:

If you are using Visual Studio then yes, you can do this.

The library builder tool that comes with Visual Studio allows you to join libraries together on the command line. I don\'t know of any way to do this in the visual editor though.

lib.exe /OUT:compositelib.lib  lib1.lib lib2.lib


回答3:

On Linux or MingW, with GNU toolchain:

ar -M <<EOM
    CREATE libab.a
    ADDLIB liba.a
    ADDLIB libb.a
    SAVE
    END
EOM
ranlib libab.a

Of if you do not delete liba.a and libb.a, you can make a \"thin archive\":

ar crsT libab.a liba.a libb.a

On Windows, with MSVC toolchain:

lib.exe /OUT:libab.lib liba.lib libb.lib


回答4:

A static library is just an archive of .o object files. Extract them with ar (assuming Unix) and pack them back into one big library.



回答5:

Alternatively to Link Library Dependencies in project properties there is another way to link libraries in Visual Studio.

  1. Open the project of the library (X) that you want to be combined with other libraries.
  2. Add the other libraries you want combined with X (Right Click, Add Existing Item...).
  3. Go to their properties and make sure Item Type is Library

This will include the other libraries in X as if you ran

lib /out:X.lib X.lib other1.lib other2.lib


回答6:

Note before you read the rest: The shell script shown here is certainly not safe to use and well tested. Use at your own risk!

I wrote a bash script to accomplish that task. Suppose your library is lib1 and the one you need to include some symbols from is lib2. The script now runs in a loop, where it first checks which undefined symbols from lib1 can be found in lib2. It then extracts the corresponding object files from lib2 with ar, renames them a bit, and puts them into lib1. Now there may be more missing symbols, because the stuff you included from lib2 needs other stuff from lib2, which we haven\'t included yet, so the loop needs to run again. If after some passes of the loop there are no changes anymore, i.e. no object files from lib2 added to lib1, the loop can stop.

Note, that the included symbols are still reported as undefined by nm, so I\'m keeping track of the object files, that were added to lib1, themselves, in order to determine whether the loop can be stopped.

#! /bin/bash

lib1=\"$1\"
lib2=\"$2\"

if [ ! -e $lib1.backup ]; then
    echo backing up
    cp $lib1 $lib1.backup
fi

remove_later=\"\"

new_tmp_file() {
    file=$(mktemp)
    remove_later=\"$remove_later $file\"
    eval $1=$file
}
remove_tmp_files() {
    rm $remove_later
}
trap remove_tmp_files EXIT

find_symbols() {
    nm $1 $2 | cut -c20- | sort | uniq 
}

new_tmp_file lib2symbols
new_tmp_file currsymbols

nm $lib2 -s --defined-only > $lib2symbols

prefix=\"xyz_import_\"
pass=0
while true; do
    ((pass++))
    echo \"Starting pass #$pass\"
    curr=$lib1
    find_symbols $curr \"--undefined-only\" > $currsymbols
    changed=0
    for sym in $(cat $currsymbols); do
        for obj in $(egrep \"^$sym in .*\\.o\" $lib2symbols | cut -d\" \" -f3); do
            echo \"  Found $sym in $obj.\"
            if [ -e \"$prefix$obj\" ]; then continue; fi
            echo \"    -> Adding $obj to $lib1\"
            ar x $lib2 $obj
            mv $obj \"$prefix$obj\"
            ar -r -s $lib1 \"$prefix$obj\"
            remove_later=\"$remove_later $prefix$obj\"
            ((changed=changed+1))
        done
    done
    echo \"Found $changed changes in pass #$pass\"

    if [[ $changed == 0 ]]; then break; fi
done

I named that script libcomp, so you can call it then e.g. with

./libcomp libmylib.a libwhatever.a

where libwhatever is where you want to include symbols from. However, I think it\'s safest to copy everything into a separate directory first. I wouldn\'t trust my script so much (however, it worked for me; I could include libgsl.a into my numerics library with that and leave out that -lgsl compiler switch).



标签: c++ linker ar .a