librelane 3.0.0.dev29__tar.gz → 3.0.0.dev32__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of librelane might be problematic. Click here for more details.
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/PKG-INFO +1 -1
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/__main__.py +29 -25
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/__init__.py +1 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/misc.py +33 -1
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/container.py +48 -27
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/env_info.py +129 -115
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/flows/flow.py +9 -13
- librelane-3.0.0.dev32/librelane/help/__main__.py +39 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/openroad.py +1 -1
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/tclstep.py +2 -1
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/pyproject.toml +2 -1
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/Readme.md +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/__init__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/__version__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/cli.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/drc.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/generic_dict.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/metrics/__init__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/metrics/__main__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/metrics/library.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/metrics/metric.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/metrics/util.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/ring_buffer.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/tcl.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/toolbox.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/tpe.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/common/types.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/config/__init__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/config/__main__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/config/config.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/config/flow.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/config/pdk_compat.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/config/preprocessor.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/config/removals.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/config/variable.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm/config.yaml +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm/pin_order.cfg +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm/src/impl.sdc +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm/src/signoff.sdc +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm/src/spm.v +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm/verify/spm_tb.v +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm-user_project_wrapper/SPM_example.v +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm-user_project_wrapper/base_sdc_file.sdc +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm-user_project_wrapper/config-tut.json +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm-user_project_wrapper/config.json +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm-user_project_wrapper/defines.v +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm-user_project_wrapper/template.def +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/examples/spm-user_project_wrapper/user_project_wrapper.v +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/flows/__init__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/flows/builtins.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/flows/classic.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/flows/cli.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/flows/misc.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/flows/optimizing.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/flows/sequential.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/flows/synth_explore.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/logging/__init__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/logging/logger.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/pdk_hashes.yaml +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/plugins.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/py.typed +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/base.sdc +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/klayout/Readme.md +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/klayout/open_design.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/klayout/render.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/klayout/stream_out.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/klayout/xml_drc_report_to_json.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/klayout/xor.drc +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/Readme.md +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/common/read.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/def/antenna_check.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/def/mag.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/def/mag_gds.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/drc.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/extract_spice.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/gds/drc_batch.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/gds/erase_box.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/gds/extras_mag.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/gds/mag_with_pointers.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/get_bbox.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/lef/extras_maglef.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/lef/maglef.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/lef.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/open.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/magic/wrapper.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/netgen/setup.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/apply_def_template.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/cell_frequency.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/check_antenna_properties.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/contextualize.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/defutil.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/diodes.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/disconnected_pins.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/eco_buffer.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/eco_diode.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/filter_unannotated.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/io_place.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/ioplace_parser/__init__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/ioplace_parser/parse.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/label_macro_pins.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/lefutil.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/placers.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/power_utils.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/random_place.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/reader.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/remove_buffers.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/snap_to_grid.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/odbpy/wire_lengths.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/antenna_check.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/antenna_repair.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/basic_mp.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/buffer_list.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/common/dpl.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/common/dpl_cell_pad.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/common/grt.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/common/io.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/common/pdn_cfg.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/common/resizer.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/common/set_global_connections.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/common/set_layer_adjustments.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/common/set_power_nets.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/common/set_rc.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/common/set_routing_layers.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/cts.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/cut_rows.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/dpl.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/drt.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/dump_rc.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/fill.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/floorplan.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/gpl.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/grt.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/gui.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/insert_buffer.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/ioplacer.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/irdrop.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/pdn.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/rcx.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/repair_design.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/repair_design_postgrt.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/rsz_timing_postcts.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/rsz_timing_postgrt.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/sta/check_macro_instances.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/sta/corner.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/tapcell.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/ungpl.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/write_cdl.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/openroad/write_views.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/pyosys/construct_abc_script.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/pyosys/json_header.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/pyosys/synthesize.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/pyosys/ys_common.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/scripts/tclsh/hello.tcl +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/state/__init__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/state/__main__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/state/design_format.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/state/state.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/__init__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/__main__.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/checker.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/common_variables.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/cvc_rv.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/klayout.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/magic.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/misc.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/netgen.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/odb.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/openroad_alerts.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/pyosys.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/step.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/verilator.py +0 -0
- {librelane-3.0.0.dev29 → librelane-3.0.0.dev32}/librelane/steps/yosys.py +0 -0
|
@@ -22,7 +22,6 @@ import shutil
|
|
|
22
22
|
import marshal
|
|
23
23
|
import tempfile
|
|
24
24
|
import traceback
|
|
25
|
-
import subprocess
|
|
26
25
|
from textwrap import dedent
|
|
27
26
|
from functools import partial
|
|
28
27
|
from typing import Any, Dict, Sequence, Tuple, Type, Optional, List
|
|
@@ -275,20 +274,12 @@ def run_included_example(
|
|
|
275
274
|
if os.path.isdir(final_path):
|
|
276
275
|
print(f"A directory named {value} already exists.", file=sys.stderr)
|
|
277
276
|
ctx.exit(1)
|
|
278
|
-
# 1. Copy the files
|
|
279
|
-
shutil.copytree(
|
|
280
|
-
example_path,
|
|
281
|
-
final_path,
|
|
282
|
-
symlinks=False,
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
# 2. Make files writable
|
|
286
|
-
if os.name == "posix":
|
|
287
|
-
subprocess.check_call(["chmod", "-R", "755", final_path])
|
|
288
277
|
|
|
278
|
+
# 1. Copy the files
|
|
279
|
+
common.recreate_tree(example_path, final_path)
|
|
289
280
|
config_file = glob.glob(os.path.join(final_path, "config.*"))[0]
|
|
290
281
|
|
|
291
|
-
#
|
|
282
|
+
# 2. Run
|
|
292
283
|
run(
|
|
293
284
|
ctx,
|
|
294
285
|
config_files=[config_file],
|
|
@@ -318,28 +309,38 @@ def cli_in_container(
|
|
|
318
309
|
if not value:
|
|
319
310
|
return
|
|
320
311
|
|
|
321
|
-
|
|
322
|
-
|
|
312
|
+
mounts = list(ctx.params.get("docker_mounts") or ())
|
|
313
|
+
tty: bool = ctx.params.get("docker_tty", True)
|
|
323
314
|
pdk_root = ctx.params.get("pdk_root")
|
|
324
|
-
|
|
315
|
+
|
|
316
|
+
try:
|
|
317
|
+
containerized_index = sys.argv.index("--dockerized")
|
|
318
|
+
except ValueError:
|
|
319
|
+
containerized_index = sys.argv.index("--containerized")
|
|
320
|
+
|
|
321
|
+
argv = sys.argv[containerized_index + 1 :]
|
|
325
322
|
|
|
326
323
|
final_argv = ["zsh"]
|
|
327
324
|
if len(argv) != 0:
|
|
328
|
-
final_argv = ["librelane"] + argv
|
|
325
|
+
final_argv = ["python3", "-m", "librelane"] + argv
|
|
329
326
|
|
|
330
|
-
|
|
327
|
+
container_image = os.getenv(
|
|
331
328
|
"LIBRELANE_IMAGE_OVERRIDE", f"ghcr.io/librelane/librelane:{__version__}"
|
|
332
329
|
)
|
|
333
330
|
|
|
334
331
|
try:
|
|
335
332
|
run_in_container(
|
|
336
|
-
|
|
333
|
+
container_image,
|
|
337
334
|
final_argv,
|
|
338
335
|
pdk_root=pdk_root,
|
|
339
|
-
other_mounts=
|
|
340
|
-
tty=
|
|
336
|
+
other_mounts=mounts,
|
|
337
|
+
tty=tty,
|
|
341
338
|
)
|
|
339
|
+
except ValueError as e:
|
|
340
|
+
err(e)
|
|
341
|
+
ctx.exit(1)
|
|
342
342
|
except Exception as e:
|
|
343
|
+
traceback.print_exc(file=sys.stderr)
|
|
343
344
|
err(e)
|
|
344
345
|
ctx.exit(1)
|
|
345
346
|
|
|
@@ -374,25 +375,28 @@ o = partial(option, show_default=True)
|
|
|
374
375
|
"Containerization options",
|
|
375
376
|
o(
|
|
376
377
|
"--docker-mount",
|
|
378
|
+
"--container-mount",
|
|
377
379
|
"-m",
|
|
378
380
|
"docker_mounts",
|
|
379
381
|
multiple=True,
|
|
380
|
-
is_eager=True, #
|
|
382
|
+
is_eager=True, # container options should be processed before anything else
|
|
381
383
|
default=(),
|
|
382
|
-
help="Used to mount more directories in dockerized mode. If a valid directory is specified, it will be mounted in the same path in the container. Otherwise, the value of the option will be passed to the
|
|
384
|
+
help="Used to mount more directories in dockerized mode. If a valid directory is specified, it will be mounted in the same path in the container. Otherwise, the value of the option will be passed to the container engine verbatim. Must be passed before --containerized/--dockerized, has no effect if not set.",
|
|
383
385
|
),
|
|
384
386
|
o(
|
|
385
387
|
"--docker-tty/--docker-no-tty",
|
|
386
|
-
|
|
388
|
+
"--container-tty/--container-no-tty",
|
|
389
|
+
is_eager=True, # container options should be processed before anything else
|
|
387
390
|
default=True,
|
|
388
|
-
help="Controls the allocation of a virtual terminal by passing -t to the Docker-compatible container engine invocation. Must be passed before --dockerized, has no effect if
|
|
391
|
+
help="Controls the allocation of a virtual terminal by passing -t to the Docker-compatible container engine invocation. Must be passed before --containerized/--dockerized, has no effect if not set.",
|
|
389
392
|
),
|
|
390
393
|
o(
|
|
391
394
|
"--dockerized",
|
|
395
|
+
"--containerized",
|
|
392
396
|
default=False,
|
|
393
397
|
is_flag=True,
|
|
394
398
|
is_eager=True, # docker options should be processed before anything else
|
|
395
|
-
help="Run the remaining flags using a
|
|
399
|
+
help="Run the remaining flags using a containerized version of LibreLane. Some caveats apply. Must precede all options except --{docker,container}-mount, --{docker,container}-[no-]tty.",
|
|
396
400
|
callback=cli_in_container,
|
|
397
401
|
),
|
|
398
402
|
)
|
|
@@ -21,6 +21,7 @@ import re
|
|
|
21
21
|
import glob
|
|
22
22
|
import gzip
|
|
23
23
|
import yaml
|
|
24
|
+
import shutil
|
|
24
25
|
import typing
|
|
25
26
|
import pathlib
|
|
26
27
|
import fnmatch
|
|
@@ -328,6 +329,37 @@ class Filter(object):
|
|
|
328
329
|
yield input
|
|
329
330
|
|
|
330
331
|
|
|
332
|
+
def recreate_tree(
|
|
333
|
+
source: AnyPath,
|
|
334
|
+
target: AnyPath,
|
|
335
|
+
):
|
|
336
|
+
"""
|
|
337
|
+
This function attempts to recreate a file tree from a source path in another
|
|
338
|
+
target path.
|
|
339
|
+
|
|
340
|
+
Permissions are not copied over. Symlinks and hardlinks are followed.
|
|
341
|
+
|
|
342
|
+
Directories are not recreated unless they contain files as (grand)children.
|
|
343
|
+
|
|
344
|
+
If the source and target are the same, the function returns early and does
|
|
345
|
+
nothing.
|
|
346
|
+
|
|
347
|
+
:param source: The source file tree to replicate
|
|
348
|
+
:param target: The target path to recreate the file tree within
|
|
349
|
+
"""
|
|
350
|
+
source = os.path.abspath(source)
|
|
351
|
+
target = os.path.abspath(target)
|
|
352
|
+
if os.path.exists(target) and os.path.samefile(source, target):
|
|
353
|
+
return
|
|
354
|
+
for dirname, _, files in os.walk(source):
|
|
355
|
+
for file in files:
|
|
356
|
+
resolved = os.path.join(dirname, file)
|
|
357
|
+
resolved_target = os.path.join(target, os.path.relpath(resolved, source))
|
|
358
|
+
os.makedirs(os.path.dirname(resolved_target), exist_ok=True)
|
|
359
|
+
with open(resolved, "rb") as fi, open(resolved_target, "wb") as fo:
|
|
360
|
+
shutil.copyfileobj(fi, fo)
|
|
361
|
+
|
|
362
|
+
|
|
331
363
|
def get_latest_file(in_path: Union[str, os.PathLike], filename: str) -> Optional[Path]:
|
|
332
364
|
"""
|
|
333
365
|
:param in_path: A directory to search in
|
|
@@ -394,7 +426,7 @@ def _get_process_limit() -> int:
|
|
|
394
426
|
|
|
395
427
|
def gzopen(filename: AnyPath, mode="rt") -> IO[Any]:
|
|
396
428
|
"""
|
|
397
|
-
This
|
|
429
|
+
This function (tries to?) emulate the gzopen from the Linux Standard Base,
|
|
398
430
|
specifically this part:
|
|
399
431
|
|
|
400
432
|
If path refers to an uncompressed file, and mode refers to a read mode,
|
|
@@ -18,7 +18,6 @@ import re
|
|
|
18
18
|
import uuid
|
|
19
19
|
import shlex
|
|
20
20
|
import pathlib
|
|
21
|
-
import tempfile
|
|
22
21
|
import subprocess
|
|
23
22
|
from typing import List, NoReturn, Sequence, Optional, Union, Tuple
|
|
24
23
|
|
|
@@ -27,15 +26,15 @@ import semver
|
|
|
27
26
|
|
|
28
27
|
from .common import mkdirp
|
|
29
28
|
from .logging import err, info, warn
|
|
30
|
-
from .env_info import OSInfo
|
|
29
|
+
from .env_info import ContainerInfo, OSInfo
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
__file_dir__ = os.path.dirname(os.path.abspath(__file__))
|
|
33
32
|
|
|
34
33
|
|
|
35
34
|
def permission_args(osinfo: OSInfo) -> List[str]:
|
|
36
35
|
if (
|
|
37
36
|
osinfo.kernel == "Linux"
|
|
38
|
-
and osinfo.container_info
|
|
37
|
+
and isinstance(osinfo.container_info, ContainerInfo)
|
|
39
38
|
and osinfo.container_info.engine == "docker"
|
|
40
39
|
and not osinfo.container_info.rootless
|
|
41
40
|
):
|
|
@@ -70,9 +69,9 @@ def gui_args(osinfo: OSInfo) -> List[str]:
|
|
|
70
69
|
return args
|
|
71
70
|
|
|
72
71
|
|
|
73
|
-
def image_exists(image: str) -> bool:
|
|
72
|
+
def image_exists(ce_path: str, image: str) -> bool:
|
|
74
73
|
images = (
|
|
75
|
-
subprocess.check_output([
|
|
74
|
+
subprocess.check_output([ce_path, "images", image])
|
|
76
75
|
.decode("utf8")
|
|
77
76
|
.rstrip()
|
|
78
77
|
.split("\n")[1:]
|
|
@@ -116,14 +115,14 @@ def remote_manifest_exists(image: str) -> bool:
|
|
|
116
115
|
return True
|
|
117
116
|
|
|
118
117
|
|
|
119
|
-
def ensure_image(image: str) -> bool:
|
|
120
|
-
if image_exists(image):
|
|
118
|
+
def ensure_image(ce_path: str, image: str) -> bool:
|
|
119
|
+
if image_exists(ce_path, image):
|
|
121
120
|
return True
|
|
122
121
|
|
|
123
122
|
try:
|
|
124
|
-
subprocess.check_call([
|
|
123
|
+
subprocess.check_call([ce_path, "pull", image])
|
|
125
124
|
except subprocess.CalledProcessError:
|
|
126
|
-
err(f"Failed to pull image {image} from the container registries.")
|
|
125
|
+
err(f"Failed to pull image '{image}' from the container registries.")
|
|
127
126
|
return False
|
|
128
127
|
|
|
129
128
|
return True
|
|
@@ -150,6 +149,22 @@ def sanitize_path(path: Union[str, os.PathLike]) -> Tuple[str, str]:
|
|
|
150
149
|
return (abspath, mountable_path)
|
|
151
150
|
|
|
152
151
|
|
|
152
|
+
def container_version_error(input: str, against: str) -> Optional[str]:
|
|
153
|
+
if input == "UNKNOWN":
|
|
154
|
+
return (
|
|
155
|
+
"Could not determine version for %s. You may encounter unexpected issues."
|
|
156
|
+
)
|
|
157
|
+
if semver.compare(input, against) < 0:
|
|
158
|
+
return f"Your %s version ({input}) is out of date. You may encounter unexpected issues."
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def ubuntu_version_at_least(current: str, minimum: str) -> bool:
|
|
163
|
+
if current == "UNKNOWN":
|
|
164
|
+
return False
|
|
165
|
+
return tuple(map(int, current.split("."))) >= tuple(map(int, minimum.split(".")))
|
|
166
|
+
|
|
167
|
+
|
|
153
168
|
def run_in_container(
|
|
154
169
|
image: str,
|
|
155
170
|
args: Sequence[str],
|
|
@@ -169,20 +184,31 @@ def run_in_container(
|
|
|
169
184
|
f"Unsupported host operating system '{osinfo.kernel}'. You may encounter unexpected issues."
|
|
170
185
|
)
|
|
171
186
|
|
|
172
|
-
if osinfo.container_info
|
|
187
|
+
if not isinstance(osinfo.container_info, ContainerInfo):
|
|
173
188
|
raise FileNotFoundError("No compatible container engine found.")
|
|
174
189
|
|
|
175
|
-
|
|
176
|
-
|
|
190
|
+
ce_path = osinfo.container_info.path
|
|
191
|
+
assert ce_path is not None
|
|
192
|
+
|
|
193
|
+
engine_name = osinfo.container_info.engine.lower()
|
|
194
|
+
if engine_name == "docker":
|
|
195
|
+
if error := container_version_error(osinfo.container_info.version, "25.0.5"):
|
|
196
|
+
warn(error % engine_name)
|
|
197
|
+
elif engine_name == "podman":
|
|
198
|
+
if osinfo.distro.lower() == "ubuntu" and not ubuntu_version_at_least(
|
|
199
|
+
osinfo.distro_version, "24.04"
|
|
200
|
+
):
|
|
177
201
|
warn(
|
|
178
|
-
|
|
202
|
+
"Versions of Podman for Ubuntu before Ubuntu 24.04 are generally pretty buggy. We recommend using Docker instead if possible."
|
|
179
203
|
)
|
|
204
|
+
elif error := container_version_error(osinfo.container_info.version, "4.1.0"):
|
|
205
|
+
warn(error % engine_name)
|
|
180
206
|
else:
|
|
181
207
|
warn(
|
|
182
|
-
f"Unsupported container engine '{osinfo.container_info.
|
|
208
|
+
f"Unsupported container engine referenced by '{osinfo.container_info.path}'. You may encounter unexpected issues."
|
|
183
209
|
)
|
|
184
210
|
|
|
185
|
-
if not ensure_image(image):
|
|
211
|
+
if not ensure_image(ce_path, image):
|
|
186
212
|
raise ValueError(f"Failed to use image '{image}'.")
|
|
187
213
|
|
|
188
214
|
terminal_args = ["-i"]
|
|
@@ -222,15 +248,6 @@ def run_in_container(
|
|
|
222
248
|
mount_args += ["-v", f"{from_cwd}:{to_cwd}"]
|
|
223
249
|
mount_args += ["-w", to_cwd]
|
|
224
250
|
|
|
225
|
-
tempdir = tempfile.mkdtemp("librelane_docker")
|
|
226
|
-
|
|
227
|
-
mount_args += [
|
|
228
|
-
"-v",
|
|
229
|
-
f"{tempdir}:/tmp",
|
|
230
|
-
"-e",
|
|
231
|
-
"TMPDIR=/tmp",
|
|
232
|
-
]
|
|
233
|
-
|
|
234
251
|
if other_mounts is not None:
|
|
235
252
|
for mount in other_mounts:
|
|
236
253
|
if os.path.isdir(mount):
|
|
@@ -242,9 +259,13 @@ def run_in_container(
|
|
|
242
259
|
|
|
243
260
|
container_id = str(uuid.uuid4())
|
|
244
261
|
|
|
262
|
+
if os.getenv("_MOUNT_HOST_LIBRELANE") == "1":
|
|
263
|
+
host_librelane_pythonpath = os.path.dirname(__file_dir__)
|
|
264
|
+
mount_args += ["-v", f"{host_librelane_pythonpath}:/host_librelane"]
|
|
265
|
+
|
|
245
266
|
cmd = (
|
|
246
267
|
[
|
|
247
|
-
|
|
268
|
+
ce_path,
|
|
248
269
|
"run",
|
|
249
270
|
"--rm",
|
|
250
271
|
"--name",
|
|
@@ -261,4 +282,4 @@ def run_in_container(
|
|
|
261
282
|
info("Running containerized command:")
|
|
262
283
|
print(shlex.join(cmd))
|
|
263
284
|
|
|
264
|
-
os.execlp(
|
|
285
|
+
os.execlp(ce_path, *cmd)
|
|
@@ -23,17 +23,16 @@ import os
|
|
|
23
23
|
import re
|
|
24
24
|
import sys
|
|
25
25
|
import json
|
|
26
|
+
import shutil
|
|
26
27
|
import tempfile
|
|
27
28
|
import platform
|
|
28
29
|
import subprocess
|
|
29
30
|
|
|
30
31
|
try:
|
|
31
|
-
from typing import Optional, Dict, List # noqa: F401
|
|
32
|
+
from typing import Union, Optional, Dict, List # noqa: F401
|
|
32
33
|
except ImportError:
|
|
33
34
|
pass
|
|
34
35
|
|
|
35
|
-
CONTAINER_ENGINE = os.getenv("OPENLANE_CONTAINER_ENGINE", "docker")
|
|
36
|
-
|
|
37
36
|
|
|
38
37
|
class StringRepresentable(object):
|
|
39
38
|
def __str__(self):
|
|
@@ -44,6 +43,7 @@ class StringRepresentable(object):
|
|
|
44
43
|
|
|
45
44
|
|
|
46
45
|
class ContainerInfo(StringRepresentable):
|
|
46
|
+
path = None # type: Optional[str]
|
|
47
47
|
engine = "UNKNOWN" # type: str
|
|
48
48
|
version = "UNKNOWN" # type: str
|
|
49
49
|
conmon = False # type: bool
|
|
@@ -54,63 +54,84 @@ class ContainerInfo(StringRepresentable):
|
|
|
54
54
|
self.version = "UNKNOWN"
|
|
55
55
|
self.conmon = False
|
|
56
56
|
self.rootless = False
|
|
57
|
+
self.seccomp = False
|
|
58
|
+
self.selinux = False
|
|
59
|
+
self.apparmor = False
|
|
57
60
|
|
|
58
61
|
@staticmethod
|
|
59
62
|
def get():
|
|
60
|
-
# type: () ->
|
|
63
|
+
# type: () -> Union[ContainerInfo, str]
|
|
64
|
+
cinfo = ContainerInfo()
|
|
65
|
+
# Here are the rules:
|
|
66
|
+
# 1. If LIBRELANE_CONTAINER_ENGINE exists, use that uncritically.
|
|
67
|
+
# 2. Else, if OPENLANE_CONTAINER_ENGINE exists, use that uncritically.
|
|
68
|
+
# 3. Else, if "docker" is in PATH, always use it.
|
|
69
|
+
# 4. Else, see if "podman" is in PATH, and use THAT.
|
|
70
|
+
# 5. If none exist, halt and return early.
|
|
71
|
+
|
|
72
|
+
container_engine = os.getenv(
|
|
73
|
+
"LIBRELANE_CONTAINER_ENGINE", os.getenv("OPENLANE_CONTAINER_ENGINE")
|
|
74
|
+
)
|
|
75
|
+
if container_engine is None or container_engine == "":
|
|
76
|
+
container_engine = shutil.which("docker")
|
|
77
|
+
if container_engine is None:
|
|
78
|
+
container_engine = shutil.which("podman")
|
|
79
|
+
if container_engine is None:
|
|
80
|
+
return "no compatible container engine found in PATH (tried docker, podman)"
|
|
61
81
|
try:
|
|
62
|
-
|
|
82
|
+
info_str = subprocess.check_output(
|
|
83
|
+
[container_engine, "info", "--format", "{{json .}}"]
|
|
84
|
+
).decode("utf8")
|
|
85
|
+
except Exception as e:
|
|
86
|
+
return "failed to get container engine info: %s" % str(e)
|
|
87
|
+
cinfo.path = container_engine
|
|
63
88
|
|
|
89
|
+
try:
|
|
90
|
+
info = json.loads(info_str)
|
|
91
|
+
except Exception as e:
|
|
92
|
+
return "result from '%s info' was not valid JSON: %s" % (
|
|
93
|
+
container_engine,
|
|
94
|
+
str(e),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if (
|
|
98
|
+
info.get("Docker Root Dir") is not None
|
|
99
|
+
or info.get("DockerRootDir") is not None
|
|
100
|
+
):
|
|
101
|
+
cinfo.engine = "docker"
|
|
102
|
+
|
|
103
|
+
# Get Version
|
|
64
104
|
try:
|
|
65
|
-
|
|
66
|
-
[
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
105
|
+
version_output = (
|
|
106
|
+
subprocess.check_output([container_engine, "--version"])
|
|
107
|
+
.decode("utf8")
|
|
108
|
+
.strip()
|
|
109
|
+
)
|
|
110
|
+
cinfo.version = re.split(r"\s", version_output)[2].strip(",")
|
|
111
|
+
except Exception:
|
|
112
|
+
pass
|
|
70
113
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
cinfo.engine = "docker"
|
|
93
|
-
|
|
94
|
-
# Get Version
|
|
95
|
-
try:
|
|
96
|
-
version_output = (
|
|
97
|
-
subprocess.check_output([CONTAINER_ENGINE, "--version"])
|
|
98
|
-
.decode("utf8")
|
|
99
|
-
.strip()
|
|
100
|
-
)
|
|
101
|
-
cinfo.version = re.split(r"\s", version_output)[2].strip(",")
|
|
102
|
-
except Exception:
|
|
103
|
-
print("Could not extract Docker version.", file=sys.stderr)
|
|
104
|
-
|
|
105
|
-
security_options = info.get("SecurityOptions")
|
|
106
|
-
for option in security_options:
|
|
107
|
-
if "rootless" in option:
|
|
108
|
-
cinfo.rootless = True
|
|
109
|
-
|
|
110
|
-
return cinfo
|
|
111
|
-
except Exception as e:
|
|
112
|
-
print(e, file=sys.stderr)
|
|
113
|
-
return None
|
|
114
|
+
security_options = info.get("SecurityOptions")
|
|
115
|
+
for option in security_options:
|
|
116
|
+
if "rootless" in option:
|
|
117
|
+
cinfo.rootless = True
|
|
118
|
+
elif info.get("host") is not None:
|
|
119
|
+
host = info["host"]
|
|
120
|
+
conmon = host.get("conmon")
|
|
121
|
+
remote_socket = host.get("remoteSocket")
|
|
122
|
+
security = host.get("security")
|
|
123
|
+
if conmon is not None:
|
|
124
|
+
cinfo.conmon = True
|
|
125
|
+
if remote_socket is not None and "podman" in remote_socket["path"]:
|
|
126
|
+
cinfo.engine = "podman"
|
|
127
|
+
cinfo.version = info["version"]["Version"]
|
|
128
|
+
if security is not None:
|
|
129
|
+
cinfo.rootless = security.get("rootless", False)
|
|
130
|
+
cinfo.apparmor = security.get("apparmorEnabled", False)
|
|
131
|
+
cinfo.seccomp = security.get("seccompEnabled", False)
|
|
132
|
+
cinfo.selinux = security.get("selinuxEnabled", False)
|
|
133
|
+
|
|
134
|
+
return cinfo
|
|
114
135
|
|
|
115
136
|
|
|
116
137
|
class NixInfo(StringRepresentable):
|
|
@@ -127,73 +148,65 @@ class NixInfo(StringRepresentable):
|
|
|
127
148
|
|
|
128
149
|
@staticmethod
|
|
129
150
|
def get():
|
|
130
|
-
# type: () ->
|
|
151
|
+
# type: () -> Union[NixInfo, str]
|
|
131
152
|
ninfo = NixInfo()
|
|
153
|
+
if shutil.which("nix") is None:
|
|
154
|
+
return "nix not found in PATH"
|
|
132
155
|
try:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
ninfo.version_string = version_str.strip()
|
|
138
|
-
except Exception as e:
|
|
139
|
-
raise Exception("Failed to get Nix info: %s" % str(e)) from None
|
|
156
|
+
version_str = subprocess.check_output(["nix", "--version"], encoding="utf8")
|
|
157
|
+
ninfo.version_string = version_str.strip()
|
|
158
|
+
except Exception as e:
|
|
159
|
+
return "could not get nix version: %s" % str(e)
|
|
140
160
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
161
|
+
try:
|
|
162
|
+
channels = {}
|
|
163
|
+
channels_raw = subprocess.check_output(
|
|
164
|
+
["nix-channel", "--list"], encoding="utf8"
|
|
165
|
+
)
|
|
166
|
+
for channel in channels_raw.splitlines():
|
|
167
|
+
name, url = channel.split(maxsplit=1)
|
|
168
|
+
channels[name] = url
|
|
169
|
+
ninfo.channels = channels
|
|
170
|
+
except Exception:
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
with tempfile.TemporaryDirectory(prefix="librelane_env_report_") as d:
|
|
174
|
+
with open(os.path.join(d, "flake.nix"), "w") as f:
|
|
175
|
+
f.write("{}")
|
|
176
|
+
nix_command = subprocess.run(
|
|
177
|
+
["nix", "eval"],
|
|
178
|
+
stdout=subprocess.PIPE,
|
|
179
|
+
stderr=subprocess.STDOUT,
|
|
180
|
+
cwd=d,
|
|
181
|
+
encoding="utf8",
|
|
182
|
+
)
|
|
183
|
+
nix_command_result = nix_command.stdout
|
|
184
|
+
if "'nix-command'" in nix_command_result:
|
|
185
|
+
pass
|
|
186
|
+
elif "'flakes'" in nix_command_result:
|
|
187
|
+
ninfo.nix_command = True
|
|
188
|
+
elif "lacks attribute" in nix_command_result:
|
|
189
|
+
ninfo.nix_command = True
|
|
190
|
+
ninfo.flakes = True
|
|
191
|
+
else:
|
|
151
192
|
print(
|
|
152
|
-
"
|
|
193
|
+
"'nix flake' returned unexpected output: %s" % nix_command_result,
|
|
153
194
|
file=sys.stderr,
|
|
154
195
|
)
|
|
155
196
|
|
|
156
|
-
|
|
157
|
-
with open(os.path.join(d, "flake.nix"), "w") as f:
|
|
158
|
-
f.write("{}")
|
|
159
|
-
nix_command = subprocess.run(
|
|
160
|
-
["nix", "eval"],
|
|
161
|
-
stdout=subprocess.PIPE,
|
|
162
|
-
stderr=subprocess.STDOUT,
|
|
163
|
-
cwd=d,
|
|
164
|
-
encoding="utf8",
|
|
165
|
-
)
|
|
166
|
-
nix_command_result = nix_command.stdout
|
|
167
|
-
if "'nix-command'" in nix_command_result:
|
|
168
|
-
pass
|
|
169
|
-
elif "'flakes'" in nix_command_result:
|
|
170
|
-
ninfo.nix_command = True
|
|
171
|
-
elif "lacks attribute" in nix_command_result:
|
|
172
|
-
ninfo.nix_command = True
|
|
173
|
-
ninfo.flakes = True
|
|
174
|
-
else:
|
|
175
|
-
print(
|
|
176
|
-
"'nix flake' returned unexpected output: %s"
|
|
177
|
-
% nix_command_result,
|
|
178
|
-
file=sys.stderr,
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
return ninfo
|
|
182
|
-
except Exception as e:
|
|
183
|
-
print(e, file=sys.stderr)
|
|
184
|
-
return None
|
|
197
|
+
return ninfo
|
|
185
198
|
|
|
186
199
|
|
|
187
200
|
class OSInfo(StringRepresentable):
|
|
188
201
|
kernel = "" # type: str
|
|
189
202
|
kernel_version = "" # type: str
|
|
190
203
|
supported = False # type: bool
|
|
191
|
-
distro =
|
|
192
|
-
distro_version =
|
|
204
|
+
distro = "UNKNOWN" # type: str
|
|
205
|
+
distro_version = "UNKNOWN" # type: str
|
|
193
206
|
python_version = "" # type: str
|
|
194
207
|
python_path = [] # type: List[str]
|
|
195
|
-
container_info = None # type:
|
|
196
|
-
nix_info = None # type:
|
|
208
|
+
container_info = None # type: Union[ContainerInfo, str]
|
|
209
|
+
nix_info = None # type: Union[NixInfo, str]
|
|
197
210
|
|
|
198
211
|
def __init__(self):
|
|
199
212
|
self.kernel = platform.system()
|
|
@@ -201,8 +214,8 @@ class OSInfo(StringRepresentable):
|
|
|
201
214
|
platform.release()
|
|
202
215
|
) # Unintuitively enough, it's the kernel's release
|
|
203
216
|
self.supported = self.kernel in ["Darwin", "Linux"]
|
|
204
|
-
self.distro =
|
|
205
|
-
self.distro_version =
|
|
217
|
+
self.distro = "UNKNOWN"
|
|
218
|
+
self.distro_version = "UNKNOWN"
|
|
206
219
|
self.python_version = platform.python_version()
|
|
207
220
|
self.python_path = sys.path.copy()
|
|
208
221
|
self.tkinter = False
|
|
@@ -212,8 +225,8 @@ class OSInfo(StringRepresentable):
|
|
|
212
225
|
self.tkinter = True
|
|
213
226
|
except ImportError:
|
|
214
227
|
pass
|
|
215
|
-
self.container_info =
|
|
216
|
-
self.nix_info =
|
|
228
|
+
self.container_info = ""
|
|
229
|
+
self.nix_info = ""
|
|
217
230
|
|
|
218
231
|
@staticmethod
|
|
219
232
|
def get():
|
|
@@ -253,13 +266,14 @@ class OSInfo(StringRepresentable):
|
|
|
253
266
|
|
|
254
267
|
config[key] = value
|
|
255
268
|
|
|
256
|
-
osinfo.distro =
|
|
257
|
-
|
|
258
|
-
|
|
269
|
+
osinfo.distro = (
|
|
270
|
+
config.get("ID") or config.get("DISTRIB_ID") or "UNKNOWN"
|
|
271
|
+
)
|
|
272
|
+
osinfo.distro_version = (
|
|
273
|
+
config.get("VERSION_ID")
|
|
274
|
+
or config.get("DISTRIB_RELEASE")
|
|
275
|
+
or "UNKNOWN"
|
|
259
276
|
)
|
|
260
|
-
|
|
261
|
-
else:
|
|
262
|
-
print("Failed to get distribution info.", file=sys.stderr)
|
|
263
277
|
|
|
264
278
|
osinfo.container_info = ContainerInfo.get()
|
|
265
279
|
osinfo.nix_info = NixInfo.get()
|