summaryrefslogtreecommitdiff
path: root/site_scons/site_tools/separate_debug.py
blob: 3edd50518e12c2f5e86ee550da9bb116269f2e39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# Copyright 2018 MongoDB Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import SCons

def _update_builder(env, builder, bitcode):

    old_scanner = builder.target_scanner
    old_path_function = old_scanner.path_function

    def new_scanner(node, env, path=()):
        results = old_scanner.function(node, env, path)
        origin = getattr(node.attributes, "debug_file_for", None)
        if origin:
            origin_results = old_scanner(origin, env, path)
            for origin_result in origin_results:
                origin_result_debug_file = getattr(origin_result.attributes, "separate_debug_file", None)
                if origin_result_debug_file:
                    results.append(origin_result_debug_file)
        # TODO: Do we need to do the same sort of drag along for bcsymbolmap files?
        return results

    builder.target_scanner = SCons.Scanner.Scanner(
        function=new_scanner,
        path_function=old_path_function,
    )

    base_action = builder.action
    if not isinstance(base_action, SCons.Action.ListAction):
        base_action = SCons.Action.ListAction([base_action])

    # TODO: Make variables for dsymutil and strip, and for the action
    # strings. We should really be running these tools as found by
    # xcrun by default. We should achieve that by upgrading the
    # site_scons/site_tools/xcode.py tool to search for these for
    # us. We could then also remove a lot of the compiler and sysroot
    # setup from the etc/scons/xcode_*.vars files, which would be a
    # win as well.
    if env.TargetOSIs('darwin'):
        if bitcode:
            base_action.list.append(
                SCons.Action.Action(
                    "dsymutil $TARGET --symbol-map=${TARGET}.bcsymbolmap -o ${TARGET}.dSYM",
                    "Generating debug info for $TARGET into ${TARGET}.dSYM"
                )
            )

        else:
            base_action.list.append(
                SCons.Action.Action(
                    "dsymutil $TARGET -o ${TARGET}.dSYM",
                    "Generating debug info for $TARGET into ${TARGET}.dSYM"
                )
            )
        base_action.list.append(
            SCons.Action.Action(
                "strip -Sx ${TARGET}",
                "Stripping ${TARGET}"
            )
        )
    elif env.TargetOSIs('posix'):
        base_action.list.extend([
            SCons.Action.Action(
                "${OBJCOPY} --only-keep-debug $TARGET ${TARGET}.debug",
                "Generating debug info for $TARGET into ${TARGET}.debug"
            ),
            SCons.Action.Action(
                "${OBJCOPY} --strip-debug --add-gnu-debuglink ${TARGET}.debug ${TARGET}",
                "Stripping debug info from ${TARGET} and adding .gnu.debuglink to ${TARGET}.debug"
            ),
        ])
    else:
        pass

    base_emitter = builder.emitter
    def new_emitter(target, source, env):

        bitcode_file = None
        if env.TargetOSIs('darwin'):
            debug_file = env.Dir(str(target[0]) + ".dSYM")
            if bitcode:
                bitcode_file = env.File(str(target[0]) + ".bcsymbolmap")
        elif env.TargetOSIs('posix'):
            debug_file = env.File(str(target[0]) + ".debug")
        else:
            pass

        setattr(debug_file.attributes, "debug_file_for", target[0])
        setattr(target[0].attributes, "separate_debug_file", debug_file)

        target.append(debug_file)

        if bitcode_file:
            setattr(bitcode_file.attributes, "bcsymbolmap_file_for", target[0])
            setattr(target[0].attributes, "bcsymbolmap_file", bitcode_file)
            target.append(bitcode_file)

        return (target, source)

    new_emitter = SCons.Builder.ListEmitter([base_emitter, new_emitter])
    builder.emitter = new_emitter

def generate(env):
    if not exists(env):
        return

    # If we are generating bitcode, add the magic linker flags that
    # hide the bitcode symbols, and override the name of the bitcode
    # symbol map file so that it is determinstically known to SCons
    # rather than being a UUID. We need this so that we can install it
    # under a well known name. We leave it to the evergreen
    # postprocessing to rename to the correct name. I'd like to do
    # this better, but struggled for a long time and decided that
    # later was a better time to address this. We should also consider
    # moving all bitcode setup into a separate tool.
    bitcode = False
    if env.TargetOSIs('darwin') and any(flag == "-fembed-bitcode" for flag in env['LINKFLAGS']):
        bitcode = True
        env.AppendUnique(LINKFLAGS=[
            "-Wl,-bitcode_hide_symbols",
            "-Wl,-bitcode_symbol_map,${TARGET}.bcsymbolmap",
        ])


    # TODO: For now, not doing this for programs. Need to update
    # auto_install_binaries to understand to install the debug symbol
    # for target X to the same target location as X.
    for builder in ['SharedLibrary', 'LoadableModule']:
        _update_builder(env, env['BUILDERS'][builder], bitcode)

def exists(env):
    return True