Search code examples
shared-librariesscons

SCONS: How do I carry on an action on a target in place


Let's say I want to strip all the debug symbols in the shared libraries that I build whiling keeping the original file name.

I tried to add an command in the method:

def mySharedLibrary(self, *args, **kwargs):
    # do some common work for every shared library like add a soname or append some lib files to LIBS      parameter
     target = SharedLibary(*args, **kwargs)
     target = env.Command(target,target, "objcopy --strip-debug ${SOURCE}")
     return target

I get this error: two different method was given to the same target, I guess it's because the two targets returned by env.Command and SharedLibrary are exactly the same name.

Any ideas to do this?

Thanks in advance!


Solution

  • I had the same problem and got the same error. What I had to do was to create an intermediate target/library. The intermediate and final targets each had their own library name, so SCons doesnt get confused.

    You could probably do something like the following:

    env.SharedLibrary(target = 'namePreStrip', source = 'yourSource')
    env.Command(target = 'name', source = 'namePreStrip', 'objcopy...')
    

    I used objcopy to build a library out of several libraries. Here's the actual source code I implemented:

    #
    # Build an object file out of several other source files, objects, and libraries
    # Optionally execute objcopy on the resulting library, depending if objcopyFlags
    # has been populated
    #
    # env - SCons Environment used to build, Mandatory arg
    # target - resulting library name, without LIBPREFIX and LIBSUFFIX, ej 'nsp2p',
    #          Mandatory arg
    # sourceFiles - list of '.cc' files that will be compiled and included in the
    #               resulting lib, Optional arg
    # objects - list of already compiled object files to be included in resulting lib,
    #           Optional arg
    # libraries - list of libraries to be included in resulting lib, Optional arg
    # objcopyFlags - list of flags to pass to objcopy command. objcopy will only
    #                be executed if this list is populated, Optional arg
    # 
    # One of [sourceFiles, objects, or libraries] must be specified, else nothing
    # will be performed
    #
    # Not using a custom builder because I dont like the way SCons prints the
    # entire command each time its called, even if its not going to actually
    # build anything AND I need more method args than provided by custom builders
    #
    def buildWholeArchive(self, env, target, sourceFiles, objects, libraries, objcopyFlags):
        if len(sourceFiles) == 0 and len(objects) == 0 and len(libraries) == 0:
            print "Incorrect use of buildWholeArchive, at least one of [sourceFiles | objects | librarires] must be specified, no build action will be performed"
            return None
    
        # Compile each source file
        objNodes = []
        if len(sourceFiles) > 0:
            objNodes = env.Object(source = sourceFiles)
    
        cmdList = []
        cmdList.append(env['CXX'])
        cmdList.append('-nostdlib -r -o $TARGET -Wl,--whole-archive')
        for obj in objNodes:
            cmdList.append(env.File(obj).abspath)
        for obj in objects:
            cmdList.append(env.File(obj).abspath)
        for lib in libraries:
            cmdList.append(lib)
        cmdList.append('-Wl,--no-whole-archive')
        cmd = ' '.join(cmdList)
    
        libTarget = '%s%s%s' % (env['LIBPREFIX'], target, env['LIBSUFFIX'])
    
        if len(objcopyFlags) > 0:
            # First create the library, then run objcopy on it
            objTarget = '%s%s_preObjcopy%s' % (env['LIBPREFIX'], target, env['LIBSUFFIX'])
            preObjcopyTarget = env.Command(target = objTarget, source = [], action = cmd)
            env.Depends(preObjcopyTarget, [objNodes, sourceFiles, objects, libraries])
    
            objCmdList = [env['OBJCOPY']]
            objCmdList.extend(objcopyFlags)
            objCmdList.append('$SOURCE $TARGET')
            objcopyCmd = ' '.join(objCmdList)
            archiveTarget = env.Command(target = libTarget, source = preObjcopyTarget, action = objcopyCmd)
        else:
            # Just create the library
            archiveTarget = env.Command(target = libTarget, source = [], action = cmd)
            env.Depends(archiveTarget, [objNodes, sourceFiles, objects, libraries])
    
        return archiveTarget
    

    And here is how I called it:

    sourceFiles = ['file1.cc', 'file2.cc']
    libSource = []
    if 'OcteonArchitecture' in env:
        libSource.append(lib1)
        libSource.append(lib2)
    libSource.append(lib3)
    
    objcopy = []
    if 'OcteonArchitecture' in env:
        objcopy.extend([
            '--redefine-sym calloc=ns_calloc',
            '--redefine-sym free=ns_free',
            '--redefine-sym malloc=ns_malloc',
            '--redefine-sym realloc=ns_realloc'])
    
    archiveTarget = clonedEnv.buildWholeArchive(target = libName,
                                                sourceFiles = sourceFiles,
                                                objects = [],
                                                libraries = libSource,
                                                objcopyFlags = objcopy)
    env.Alias('libMyLib', archiveTarget)