summaryrefslogtreecommitdiff
path: root/meta/classes/chrpath.bbclass
blob: 61a24b3f5a01ca44dc28b777820e83cb6666e610 (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
CHRPATH_BIN ?= "chrpath"
PREPROCESS_RELOCATE_DIRS ?= ""

def process_file_linux(cmd, fpath, basedir, tmpdir, d):
    import subprocess as sub

    p = sub.Popen([cmd, '-l', fpath],stdout=sub.PIPE,stderr=sub.PIPE)
    err, out = p.communicate()
    # If returned succesfully, process stderr for results
    if p.returncode != 0:
        return

    # Throw away everything other than the rpath list
    curr_rpath = err.partition("RPATH=")[2]
    #bb.note("Current rpath for %s is %s" % (fpath, curr_rpath.strip()))
    rpaths = curr_rpath.split(":")
    new_rpaths = []
    for rpath in rpaths:
        # If rpath is already dynamic copy it to new_rpath and continue
        if rpath.find("$ORIGIN") != -1:
            new_rpaths.append(rpath.strip())
            continue
        rpath =  os.path.normpath(rpath)
        # If the rpath shares a root with base_prefix determine a new dynamic rpath from the
        # base_prefix shared root
        if rpath.find(basedir) != -1:
            depth = fpath.partition(basedir)[2].count('/')
            libpath = rpath.partition(basedir)[2].strip()
        # otherwise (i.e. cross packages) determine a shared root based on the TMPDIR
        # NOTE: This will not work reliably for cross packages, particularly in the case
        # where your TMPDIR is a short path (i.e. /usr/poky) as chrpath cannot insert an
        # rpath longer than that which is already set.
        elif rpath.find(tmpdir) != -1:
            depth = fpath.rpartition(tmpdir)[2].count('/')
            libpath = rpath.partition(tmpdir)[2].strip()
        else:
            new_rpaths.append(rpath.strip())
            return
        base = "$ORIGIN"
        while depth > 1:
            base += "/.."
            depth-=1
        new_rpaths.append("%s%s" % (base, libpath))

    # if we have modified some rpaths call chrpath to update the binary
    if len(new_rpaths):
        args = ":".join(new_rpaths)
        #bb.note("Setting rpath for %s to %s" %(fpath, args))
        p = sub.Popen([cmd, '-r', args, fpath],stdout=sub.PIPE,stderr=sub.PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            bb.error("%s: chrpath command failed with exit code %d:\n%s%s" % (d.getVar('PN', True), p.returncode, out, err))
            raise bb.build.FuncFailed

def process_file_darwin(cmd, fpath, basedir, tmpdir, d):
    import subprocess as sub

    p = sub.Popen([d.expand("${HOST_PREFIX}otool"), '-L', fpath],stdout=sub.PIPE,stderr=sub.PIPE)
    err, out = p.communicate()
    # If returned succesfully, process stderr for results
    if p.returncode != 0:
        return
    for l in err.split("\n"):
        if "(compatibility" not in l:
            continue
        rpath = l.partition("(compatibility")[0].strip()
        if rpath.find(basedir) != -1:
            depth = fpath.partition(basedir)[2].count('/')
            libpath = rpath.partition(basedir)[2].strip()
        else:
            continue

        base = "@loader_path"
        while depth > 1:
            base += "/.."
            depth-=1
        base = base + libpath
        p = sub.Popen([d.expand("${HOST_PREFIX}install_name_tool"), '-change', rpath, base, fpath],stdout=sub.PIPE,stderr=sub.PIPE)
        err, out = p.communicate()

def process_dir (directory, d):
    import stat

    cmd = d.expand('${CHRPATH_BIN}')
    tmpdir = os.path.normpath(d.getVar('TMPDIR'))
    basedir = os.path.normpath(d.expand('${base_prefix}'))
    hostos = d.getVar("HOST_OS", True)

    #bb.debug("Checking %s for binaries to process" % directory)
    if not os.path.exists(directory):
        return

    if "linux" in hostos:
        process_file = process_file_linux
    elif "darwin" in hostos:
        process_file = process_file_darwin
    else:
        # Relocations not supported
        return

    dirs = os.listdir(directory)
    for file in dirs:
        fpath = directory + "/" + file
        fpath = os.path.normpath(fpath)
        if os.path.islink(fpath):
            # Skip symlinks
            continue

        if os.path.isdir(fpath):
            process_dir(fpath, d)
        else:
            #bb.note("Testing %s for relocatability" % fpath)

            # We need read and write permissions for chrpath, if we don't have
            # them then set them temporarily. Take a copy of the files
            # permissions so that we can restore them afterwards.
            perms = os.stat(fpath)[stat.ST_MODE]
            if os.access(fpath, os.W_OK|os.R_OK):
                perms = None
            else:
                # Temporarily make the file writeable so we can chrpath it
                os.chmod(fpath, perms|stat.S_IRWXU)
            process_file(cmd, fpath, basedir, tmpdir, d)
                
            if perms:
                os.chmod(fpath, perms)

def rpath_replace (path, d):
    bindirs = d.expand("${bindir} ${sbindir} ${base_sbindir} ${base_bindir} ${libdir} ${base_libdir} ${libexecdir} ${PREPROCESS_RELOCATE_DIRS}").split()

    for bindir in bindirs:
        #bb.note ("Processing directory " + bindir)
        directory = path + "/" + bindir
        process_dir (directory, d)