File: C:/Users/fred/anaconda3/Lib/site-packages/conda_build/noarch_python.py
# Copyright (C) 2014 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import annotations
import json
import locale
import logging
import os
import shutil
from os.path import basename, dirname, isfile, join
from pathlib import Path
from .exceptions import CondaBuildUserError
from .utils import bin_dirname, on_win, rm_rf
def rewrite_script(fn: str, prefix: str | os.PathLike) -> str:
"""Take a file from the bin directory and rewrite it into the python-scripts
directory with the same permissions after it passes some sanity checks for
noarch pacakges"""
# Load and check the source file for not being a binary
src = Path(prefix, bin_dirname, fn)
encoding = locale.getpreferredencoding()
# if default locale is ascii, allow UTF-8 (a reasonably modern ASCII extension)
if encoding == "ANSI_X3.4-1968":
encoding = "UTF-8"
try:
data = src.read_text(encoding=encoding)
except UnicodeDecodeError: # binary file
raise CondaBuildUserError(f"Noarch package contains binary script: {fn}")
src_mode = src.stat().st_mode
rm_rf(src)
# Get rid of '-script.py' suffix on Windows
if on_win and fn.endswith("-script.py"):
fn = fn[:-10]
# Rewrite the file to the python-scripts directory
dst_dir = Path(prefix, "python-scripts")
dst_dir.mkdir(exist_ok=True)
dst = dst_dir / fn
dst.write_text(data)
dst.chmod(src_mode)
return fn
def handle_file(f, d, prefix):
"""Process a file for inclusion in a noarch python package."""
path = join(prefix, f)
# Ignore egg-info and pyc files.
if f.endswith((".egg-info", ".pyc", ".pyo")):
os.unlink(path)
elif f.endswith(".exe") and (
isfile(os.path.join(prefix, f[:-4] + "-script.py"))
or basename(f[:-4]) in d["python-scripts"]
):
os.unlink(path) # this is an entry point with a matching xx-script.py
elif "site-packages" in f:
nsp = join(prefix, "site-packages")
os.makedirs(nsp, exist_ok=True)
g = f[f.find("site-packages") :]
dst = join(prefix, g)
dst_dir = dirname(dst)
os.makedirs(dst_dir, exist_ok=True)
shutil.move(path, dst)
d["site-packages"].append(g[14:])
# Treat scripts specially with the logic from above
elif f.startswith(("bin/", "Scripts")):
fn = basename(path)
fn = rewrite_script(fn, prefix)
d["python-scripts"].append(fn)
# Include examples in the metadata doc
elif f.startswith(("Examples/", "Examples\\")):
d["Examples"].append(f[9:])
# No special treatment for other files
# leave them as-is
else:
# this should be the built-in logging module, not conda-build's stuff, because this file is standalone.
log = logging.getLogger(__name__)
log.debug(f"Don't know how to handle file: {f}. Including it as-is.")
def populate_files(m, files, prefix, entry_point_scripts=None):
d = {"dist": m.dist(), "site-packages": [], "python-scripts": [], "Examples": []}
# Populate site-package, python-scripts, and Examples into above
for f in files:
handle_file(f, d, prefix)
# Windows path conversion
if on_win:
for fns in (d["site-packages"], d["Examples"]):
for i, fn in enumerate(fns):
fns[i] = fn.replace("\\", "/")
if entry_point_scripts:
for entry_point in entry_point_scripts:
src = join(prefix, entry_point)
if os.path.isfile(src):
os.unlink(src)
return d
def transform(m, files, prefix):
bin_dir = join(prefix, "bin")
os.makedirs(bin_dir, exist_ok=True)
scripts_dir = join(prefix, "Scripts")
os.makedirs(scripts_dir, exist_ok=True)
name = m.name()
# Create *nix prelink script
# Note: it's important to use LF newlines or it wont work if we build on Win
with open(join(bin_dir, f".{name}-pre-link.sh"), "wb") as fo:
fo.write(
b"""\
#!/bin/bash
$PREFIX/bin/python $SOURCE_DIR/link.py
"""
)
# Create windows prelink script (be nice and use Windows newlines)
with open(join(scripts_dir, f".{name}-pre-link.bat"), "wb") as fo:
fo.write(
"""\
@echo off
"%PREFIX%\\python.exe" "%SOURCE_DIR%\\link.py"
""".replace("\n", "\r\n").encode("utf-8")
)
d = populate_files(m, files, prefix)
# Find our way to this directory
this_dir = dirname(__file__)
# copy in windows exe shims if there are any python-scripts
if d["python-scripts"]:
for fn in "cli-32.exe", "cli-64.exe":
shutil.copyfile(join(this_dir, fn), join(prefix, fn))
# Read the local _link.py
with open(join(this_dir, "_link.py")) as fi:
link_code = fi.read()
# Write the package metadata, and bumper with code for linking
with open(join(prefix, "link.py"), "w") as fo:
fo.write("DATA = ")
json.dump(d, fo, indent=2, sort_keys=True)
fo.write("\n## END DATA\n\n")
fo.write(link_code)