File: read_env.py

package info (click to toggle)
dials 3.25.0%2Bdfsg3-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 20,112 kB
  • sloc: python: 134,740; cpp: 34,526; makefile: 160; sh: 142
file content (192 lines) | stat: -rw-r--r-- 6,494 bytes parent folder | download
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
"""
Reads a libtbx_env file and extract information as a json string.

Some structures e.g. module dictionaries refer to the same object in multiple
places. These will be dumped multiple times. Anything that refers back to a
previous level will show as "__selfreference__". Relocatable paths are just
shown as the regular, joined path (from inspection all base off of the build
path anyway).
"""

from __future__ import annotations

import argparse
import json
import os
import pickle
import sys
from pathlib import Path
from types import ModuleType

BUILD_PATH = None
_all_relocatable_paths = set()
_all_absolute_paths = set()


def _read_obj(obj, prev=None):
    if prev is None:
        prev = []
    if obj in prev:
        return "__selfreference__"
    prev = list(prev) + [obj]

    if isinstance(obj, prop_object):
        dic = {name: _read_obj(val, prev) for name, val in obj.__dict__.items()}
        dic["__type__"] = obj._pathed_type
        return dic
    elif isinstance(obj, list):
        p = []
        for i in obj:
            p.append(_read_obj(i, prev))
        return p
    elif isinstance(obj, dict):
        return {a: _read_obj(b, prev) for a, b in obj.items()}
    else:
        return obj


class prop_object:
    """Object that can convert itself to a dictionary"""

    def to_dict(self):
        return _read_obj(self)


def pathed_prop_object(path):
    "Create a class that knows the path it's supposed to represent"

    class _pathed_prop_object(prop_object):
        """Object that can convert itself to a dictionary"""

        _pathed_type = path

    return _pathed_prop_object


class relocatable_path:
    def __repr__(self):
        _all_relocatable_paths.add(self)
        return os.path.normpath(os.path.join(str(self._anchor), self.relocatable))


class absolute_path:
    def __init__(self, path):
        # This init not used by unpickle - only for rewriting in here
        self._path = str(path)

    def __repr__(self):
        _all_absolute_paths.add(self)
        return self._path


def plainlify(thing):
    if isinstance(thing, str | int | float | complex):
        return thing
    if thing in (None, True, False):
        return thing
    if isinstance(thing, tuple):
        return tuple(map(plainlify, thing))
    if isinstance(thing, list):
        return list(map(plainlify, thing))
    if isinstance(thing, dict):
        return {plainlify(key): plainlify(value) for key, value in thing.items()}
    if isinstance(thing, set):
        return [plainlify(item) for item in thing]
    return str(thing)


def new_module(name, doc=None):
    """Create a new module and inject it into sys.modules"""
    m = ModuleType(name, doc)
    m.__file__ = name + ".py"
    sys.modules[name] = m
    return m


# Create the fake libtbx environment
libtbx = new_module("libtbx")
libtbx.env_config = new_module("libtbx.env_config")
libtbx.path = new_module("libtbx.path")
libtbx.env_config.environment = pathed_prop_object("libtbx.env_config.environment")
libtbx.env_config.build_options = pathed_prop_object("libtbx.env_config.build_options")
libtbx.env_config.module = pathed_prop_object("libtbx.env_config.module")
libtbx.path.relocatable_path = relocatable_path
libtbx.path.absolute_path = absolute_path


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Read information from a libtbx_env file"
    )
    parser.add_argument("libtbx_env", type=Path)
    parser.add_argument(
        "--build-path",
        type=Path,
        help=(
            "The actual build path. If this disagrees with the environment, "
            "relative paths will be rewritten. This is to help match the "
            "libtbx behaviour under transplanted installs (e.g. conda)."
        ),
    )
    parser.add_argument(
        "--sys-prefix",
        type=Path,
        help=(
            "The sys.prefix for the distribution the libtbx_env sits within. "
            "If set, and the libtbx_env is an installed one, will rewrite internal prefix."
        ),
    )
    parser.add_argument(
        "--windows",
        action="store_true",
        help=(
            "Apply libtbx-installed-on-windows rewrite-rules, if the libtbx "
            "environment is an installed one. "
            "On windows, libtbx transplants the system root for some paths."
        ),
    )
    args = parser.parse_args()
    if not args.libtbx_env.is_file():
        sys.exit(f"Error: {args.libtbx_env} is not a file")
    # Use the libtbx_env path as the real build_path, for rewriting paths
    BUILD_PATH = args.build_path
    # Load the environment dump
    env = pickle.loads(args.libtbx_env.read_bytes())
    # Because of pickle, we need to __repr__ everything in order to register it
    plainlify(env.to_dict())
    # Rewrite the build folder, if we've been provided one
    if args.build_path:
        orig_build_path = str(env.build_path)
        new_build_path = str(args.build_path.resolve())
        # The relocatable path uses an "absolute path" as a parent. So,
        # make sure we translate all absolute paths that exist, and this
        # means that we've moved all relocatable paths.
        for a_path in _all_absolute_paths:
            if a_path._path == orig_build_path:
                a_path._path = new_build_path

    if env.installed and args.sys_prefix:
        # If this is an installed libtbx_env, then we have rules about rewriting it
        # this is... non-ideal, but since release libtbx_env files are broken on
        # windows, this is the best place to deal with it.
        new_prefix = absolute_path(args.sys_prefix.resolve())
        if args.windows:
            new_prefix = absolute_path(args.sys_prefix.resolve() / "library")
        # Replace the anchor for each of the repository paths
        for path in env.repository_paths:
            path._anchor = new_prefix
        env.bin_path._anchor = new_prefix
        env.exe_path._anchor = new_prefix
        env.include_path._anchor = new_prefix
        env.lib_path._anchor = new_prefix
        env.path_utility._anchor = new_prefix
        # Rewrite all of the module dist paths
        if args.windows:
            for module in env.module_list:
                for dist_path in module.dist_paths:
                    if dist_path is not None:
                        dist_path._anchor = new_prefix

    # Regenerate the plain dictionary now we've rewritten the paths
    d = plainlify(env.to_dict())
    print(json.dumps(d, indent=4))