├── .github └── workflows │ ├── automatic.yml │ ├── general-ci.yml │ └── test.yml ├── .gitignore ├── Dockerfile ├── Makefile ├── README.md ├── bootlin_toolchain_downloader.py ├── compile.sh ├── configure_cross_flags.py ├── get_setup_modules.py ├── sysconfig_filter.py └── zipper.py /.github/workflows/automatic.yml: -------------------------------------------------------------------------------- 1 | 2 | name: "Automatic ci" 3 | 4 | on: 5 | push: 6 | branches: 7 | - '**' 8 | pull_request: 9 | branches: 10 | - '**' 11 | 12 | jobs: 13 | run_compile_ci: 14 | uses: ./.github/workflows/general-ci.yml 15 | run_test_ci: 16 | uses: ./.github/workflows/test.yml 17 | -------------------------------------------------------------------------------- /.github/workflows/general-ci.yml: -------------------------------------------------------------------------------- 1 | name: General CI 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | arch: 7 | type: string 8 | required: false 9 | description: "CPU Architecture" 10 | default: aarch64 11 | libc: 12 | type: string 13 | required: false 14 | description: "LIBC name" 15 | default: musl 16 | python_ver: 17 | type: string 18 | required: false 19 | description: "Python version" 20 | default: 3.9.7 21 | create_release: 22 | type: boolean 23 | required: false 24 | description: "Publish release" 25 | default: false 26 | 27 | workflow_dispatch: 28 | inputs: 29 | arch: 30 | required: true 31 | description: "CPU Architecture" 32 | default: aarch64 33 | libc: 34 | required: true 35 | description: "LIBC name" 36 | type: choice 37 | options: 38 | - musl 39 | - uclibc 40 | - glibc 41 | default: musl 42 | python_ver: 43 | type: string 44 | required: true 45 | description: "Python version" 46 | default: 3.9.7 47 | create_release: 48 | type: boolean 49 | required: true 50 | description: "Publish release" 51 | default: false 52 | 53 | jobs: 54 | 55 | build_docker: 56 | runs-on: ubuntu-latest 57 | steps: 58 | - uses: actions/checkout@v3 59 | - name: Build the Docker image 60 | run: docker build --build-arg=ARCH=${{ inputs.arch }} --build-arg=LIBC=${{ inputs.libc }} --build-arg=PYTHON_VER=${{ inputs.python_ver }} --tag static-python-maker . 61 | - name: Run the Docker image 62 | run: docker run --name static-python-container static-python-maker 63 | - name: Get output binary out of docker 64 | run: docker cp static-python-container:/app/final/static_python . 65 | - name: Upload docker result 66 | uses: actions/upload-artifact@v4 67 | with: 68 | name: static_python_docker 69 | path: ./static_python 70 | 71 | build_normal: 72 | runs-on: ubuntu-latest 73 | steps: 74 | - name: Prerequisites 75 | run: sudo apt install -y build-essential zlib1g-dev wget python3 python3-requests 76 | - uses: actions/checkout@v3 77 | - name: Get compiler 78 | run: python3 bootlin_toolchain_downloader.py ${{ inputs.arch }} ${{ inputs.libc }} --extract --make_runner 79 | - name: Make static python 80 | run: Python_VER=${{ inputs.python_ver }} ./make_runner.sh -j all 81 | - name: Upload normal result 82 | uses: actions/upload-artifact@v4 83 | with: 84 | name: static_python_normal 85 | path: ./final/static_python 86 | 87 | test_reproducibility: 88 | runs-on: ubuntu-latest 89 | needs: [build_docker, build_normal] 90 | steps: 91 | - uses: actions/download-artifact@v4 92 | with: 93 | name: static_python_docker 94 | path: docker/ 95 | - uses: actions/download-artifact@v4 96 | with: 97 | name: static_python_normal 98 | path: normal/ 99 | - name: Compare 100 | run: cmp -s normal/static_python docker/static_python 101 | 102 | 103 | test_python: 104 | runs-on: ubuntu-latest 105 | needs: [build_normal] 106 | steps: 107 | - name: Prerequisites 108 | run: sudo apt install -y qemu-user-static 109 | - uses: actions/download-artifact@v4 110 | with: 111 | name: static_python_normal 112 | - run: chmod u+x ./static_python 113 | - name: Test binary with qemu 114 | run: qemu-${{ inputs.arch }}-static ./static_python -c 'print("hello")' 115 | 116 | release_binaries: 117 | if: ${{ inputs.create_release }} 118 | runs-on: ubuntu-latest 119 | needs: [test_python, test_reproducibility, build_normal] 120 | name: Create Release 121 | steps: 122 | - name: get timestamp 123 | id: timestamp 124 | run: echo "timestamp=$(date +%Y%m%d%H%M%S%N)" >> "$GITHUB_OUTPUT" 125 | - uses: actions/download-artifact@v4 126 | with: 127 | name: static_python_normal 128 | - name: Create Release 129 | id: create_release 130 | uses: actions/create-release@v1 131 | env: 132 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 133 | with: 134 | tag_name: tag_${{ steps.timestamp.outputs.timestamp }} 135 | release_name: Static python ${{ inputs.python_ver }} for ${{ inputs.arch }}-${{ inputs.libc }} 136 | draft: false 137 | prerelease: true 138 | - name: Upload Release Asset 139 | id: upload-release-asset 140 | uses: actions/upload-release-asset@v1 141 | env: 142 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 143 | with: 144 | upload_url: ${{ steps.create_release.outputs.upload_url }} 145 | asset_path: ./static_python 146 | asset_name: static_python_${{ inputs.python_ver }}-${{ inputs.arch }}-${{ inputs.libc }} 147 | asset_content_type: application/octet-stream 148 | 149 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test CI 2 | 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | test_python3_9_7: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Prerequisites 12 | run: sudo apt install -y build-essential zlib1g-dev wget 13 | - uses: actions/checkout@v3 14 | - name: Get compiler 15 | run: wget https://github.com/sagimor6/static-glibc-toolchain/releases/download/tag_20250118014933284391032/x86_64-unknown-linux-gnu.tar.xz && tar -xjf x86_64-unknown-linux-gnu.tar.xz 16 | - name: Make static python 17 | run: MY_CROSS_ARCH="x86_64-unknown-linux-gnu" MY_CROSS_PATH="$(pwd)/x86_64-unknown-linux-gnu/bin" MODULE_BLACKLIST="_ctypes _ctypes_test ctypes" ONLY_TEST_BUILD=y Python_VER=3.9.7 make -j all 18 | - name: Run tests 19 | run: > 20 | ./final/static_python -E -m test -uall 21 | -i test.test_capi.Test_ModuleStateAccess.* 22 | -i test.test_imp.ImportTests.*_module* 23 | -i test.test_import.ImportTests.test_from_import_missing_attr_has_name_and_so_path 24 | -i test.test_importlib.extension.test_loader.* 25 | -i test.test_importlib.extension.test_finder.*_FinderTests.test_module 26 | -i test.test_email.test_utils.LocaltimeTests.test_variable_tzname 27 | -i test.test_urllib2.MiscTests.test_issue16464 28 | 29 | test_python3_13_1: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Prerequisites 33 | run: sudo apt install -y build-essential zlib1g-dev wget 34 | - uses: actions/checkout@v3 35 | - name: Get compiler 36 | run: wget https://github.com/sagimor6/static-glibc-toolchain/releases/download/tag_20250118014933284391032/x86_64-unknown-linux-gnu.tar.xz && tar -xjf x86_64-unknown-linux-gnu.tar.xz 37 | - name: Make static python 38 | run: MY_CROSS_ARCH="x86_64-unknown-linux-gnu" MY_CROSS_PATH="$(pwd)/x86_64-unknown-linux-gnu/bin" MODULE_BLACKLIST="_ctypes _ctypes_test ctypes" ONLY_TEST_BUILD=y Python_VER=3.13.1 make -j all 39 | - name: Run tests 40 | run: > 41 | ./final/static_python -E -m test -uall 42 | -i test.datetimetester.CapiTest_Fast.test_type_check_in_subinterp 43 | -i test.test_embed.* 44 | -i test.test_site._pthFileTests.test_underpth_basic 45 | -i test.test_site._pthFileTests.test_underpth_file 46 | -i test.test_site._pthFileTests.test_underpth_nosite_file 47 | -i test.test_tools.* 48 | -i test.test_venv.BasicTest.test_sysconfig 49 | -i test.test_venv.BasicTest.test_zippath_from_non_installed_posix 50 | 51 | test_python2_7_18: 52 | runs-on: ubuntu-latest 53 | steps: 54 | - name: Prerequisites 55 | run: sudo apt install -y build-essential zlib1g-dev wget 56 | - uses: actions/checkout@v3 57 | - name: Get compiler 58 | run: wget https://github.com/sagimor6/static-glibc-toolchain/releases/download/tag_20250118014933284391032/x86_64-unknown-linux-gnu.tar.xz && tar -xjf x86_64-unknown-linux-gnu.tar.xz 59 | - name: Make static python 60 | run: MY_CROSS_ARCH="x86_64-unknown-linux-gnu" MY_CROSS_PATH="$(pwd)/x86_64-unknown-linux-gnu/bin" MODULE_BLACKLIST="_ctypes _ctypes_test ctypes" ONLY_TEST_BUILD=y Python_VER=2.7.18 make -j all 61 | - name: Run tests 62 | run: > 63 | ./final/static_python -E -m test -uall -x test_ssl test_curses test_distutils 64 | - name: Run tests ssl 65 | run: > 66 | ./final/static_python -E -m test -uall 67 | -m 'test.test_ssl.[!N]*' 68 | -m 'test.test_ssl.NetworkedTests.test_[!c]*' 69 | -m 'test.test_ssl.NetworkedTests.test_c[!o]*' 70 | -m 'test.test_ssl.NetworkedTests.test_co[!n]*' 71 | -m 'test.test_ssl.NetworkedTests.test_con[!t]*' 72 | test_ssl 73 | - name: Run tests distutils 74 | run: > 75 | ./final/static_python -E -m test -uall 76 | -m 'distutils.tests.test_[!bci]*' 77 | -m 'distutils.tests.test_b[!u]*' 78 | -m 'distutils.tests.test_build.*' 79 | -m 'distutils.tests.test_build_[!ce]*' 80 | -m 'distutils.tests.test_build_clib.[!B]*' 81 | -m 'distutils.tests.test_build_clib.BuildCLibTestCase.test_[!r]*' 82 | -m 'distutils.tests.test_build_ext.[!B]*' 83 | -m 'distutils.tests.test_build_ext.BuildExtTestCase.test_[!bg]*' 84 | -m 'distutils.tests.test_build_ext.BuildExtTestCase.test_build_ext_*' 85 | -m 'distutils.tests.test_build_ext.BuildExtTestCase.test_get_[!o]*' 86 | -m 'distutils.tests.test_c[!o]*' 87 | -m 'distutils.tests.test_co[!n]*' 88 | -m 'distutils.tests.test_config.*' 89 | -m 'distutils.tests.test_config_cmd.[!C]*' 90 | -m 'distutils.tests.test_config_cmd.ConfigTestCase.test_[!s]*' 91 | -m 'distutils.tests.test_install_*' 92 | -m 'distutils.tests.test_install.[!I]*' 93 | -m 'distutils.tests.test_install.InstallTestCase.test_[!r]*' 94 | -m 'distutils.tests.test_install.InstallTestCase.test_record' 95 | -m 'distutils.[!t]*' 96 | test_distutils 97 | - name: Run tests curses 98 | run: > 99 | ./final/static_python -E -m test -uall 100 | -m 'test.test_curses.[!T]*' 101 | -m 'test.test_curses.Test[!C]*' 102 | -m 'test.test_curses.TestCurses.test_[!c]*' 103 | test_curses 104 | 105 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | final/ 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM ubuntu 3 | 4 | RUN apt-get update && apt-get install -y --no-install-recommends build-essential zlib1g-dev wget python3 python3-requests 5 | 6 | WORKDIR /app/ 7 | 8 | COPY Makefile . 9 | COPY zipper.py . 10 | COPY get_setup_modules.py . 11 | COPY bootlin_toolchain_downloader.py . 12 | COPY sysconfig_filter.py . 13 | COPY configure_cross_flags.py . 14 | 15 | ARG ARCH 16 | ARG LIBC 17 | ARG PYTHON_VER=3.9.7 18 | 19 | RUN python3 bootlin_toolchain_downloader.py $ARCH $LIBC --extract --make_runner \ 20 | && rm -f *.tar.bz2 21 | 22 | RUN apt-get purge -y python3 python3-requests && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* 23 | 24 | ENV Python_VER=$PYTHON_VER 25 | 26 | CMD ./make_runner.sh -j all 27 | 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | MY_CROSS_ARCH ?= aarch64-buildroot-linux-musl 4 | MY_CROSS_PATH ?= /opt/aarch64-buildroot-linux-musl/bin 5 | MY_CROSS_PREFIX ?= $(MY_CROSS_PATH)/$(MY_CROSS_ARCH)- 6 | MY_CROSS_OPENSSL_MACHINE ?= $(word 1, $(subst -, ,$(MY_CROSS_ARCH))) 7 | MY_CROSS_OPENSSL_LONG ?= $(word 3, $(subst -, ,$(MY_CROSS_ARCH)))-$(MY_CROSS_OPENSSL_MACHINE) 8 | 9 | OPT_CFLAGS = -ffunction-sections -fdata-sections -flto -fuse-linker-plugin -ffat-lto-objects -Os -flto=auto 10 | OPT_LDFLAGS = -flto -flto=auto -fuse-linker-plugin -ffat-lto-objects -Wl,--gc-sections -Os -flto-partition=one 11 | 12 | MODULE_BLACKLIST ?= 13 | 14 | CC ?= $(MY_CROSS_PREFIX)gcc 15 | 16 | check_cc_flag = $(if $(shell if $(CC) $(1) -Wl,--help -xc -o /dev/null /dev/null >/dev/null 2>/dev/null; then echo a; fi; ),$(1),) 17 | 18 | OPT_CFLAGS := $(foreach cflag,$(OPT_CFLAGS),$(call check_cc_flag,$(cflag))) 19 | OPT_LDFLAGS := $(foreach cflag,$(OPT_LDFLAGS),$(call check_cc_flag,$(cflag))) 20 | 21 | SRC_PATH_ABS=$(shell pwd) 22 | 23 | BUILD_DIR=build 24 | OUTPUT_DIR=final 25 | 26 | BUILD_DIR_ABS=$(SRC_PATH_ABS)/$(BUILD_DIR) 27 | 28 | PY_BUILD_DIR=$(BUILD_DIR)/pybuild_$(Python_VER) 29 | PY_BUILD_DIR_ABS=$(SRC_PATH_ABS)/$(PY_BUILD_DIR) 30 | 31 | ifeq ($(ONLY_TEST_BUILD),y) 32 | FINAL_PYTHONHOME_PATH=$(PY_BUILD_DIR_ABS)/pyfakeroot2/usr/local 33 | DO_ON_RELEASE := : 34 | else 35 | FINAL_PYTHONHOME_PATH=/proc/self/exe 36 | DO_ON_RELEASE := 37 | endif 38 | 39 | .PHONY: clean distclean download bla all 40 | clean: 41 | -rm -rf $(BUILD_DIR) $(OUTPUT_DIR) 42 | 43 | distclean: clean 44 | -rm -rf *.tar.gz *.tar.xz 45 | 46 | 47 | # .ONESHELL: 48 | # .SHELLFLAGS = -e 49 | 50 | 51 | nl:=$(strip \) 52 | 53 | define tar_xz_template = 54 | $(1)-$$($(1)_VER)_tar_xz ?= $(1)-$$($(1)_VER).tar.xz 55 | $(1)_build_dir ?= $(BUILD_DIR) 56 | 57 | $$($(1)-$$($(1)_VER)_tar_xz): 58 | wget $$($(1)_LINK)$$($(1)-$$($(1)_VER)_tar_xz) 59 | 60 | $$($(1)_build_dir)/$(1)-$$($(1)_VER)/ : $$($(1)-$$($(1)_VER)_tar_xz) | $$($(1)_build_dir)/ 61 | (set -e; $(nl) 62 | cd $$($(1)_build_dir); $(nl) 63 | tar -xJf $$(SRC_PATH_ABS)/$$^; $(nl) 64 | ) 65 | 66 | endef 67 | 68 | define tar_gz_template = 69 | $(1)-$$($(1)_VER)_tar_gz ?= $(1)-$$($(1)_VER).tar.gz 70 | $(1)_build_dir ?= $(BUILD_DIR) 71 | 72 | $$($(1)-$$($(1)_VER)_tar_gz): 73 | wget $$($(1)_LINK)$$($(1)-$$($(1)_VER)_tar_gz) 74 | 75 | $$($(1)_build_dir)/$(1)-$$($(1)_VER)/ : $$($(1)-$$($(1)_VER)_tar_gz) | $$($(1)_build_dir)/ 76 | (set -e; $(nl) 77 | cd $$($(1)_build_dir); $(nl) 78 | tar -xzf $$(SRC_PATH_ABS)/$$^; $(nl) 79 | ) 80 | 81 | endef 82 | 83 | openssl_VER ?= 1.1.1l 84 | bzip2_VER ?= 1.0.8 85 | libffi_VER ?= 3.4.2 86 | ncurses_VER ?= 6.2 87 | readline_VER ?= 8.1 88 | gdbm_VER ?= 1.23 89 | sqlite-autoconf_VER ?= 3380100 90 | Python_VER ?= 3.9.7 91 | xz_VER ?= 5.2.5 92 | zlib_VER ?= 1.2.12 93 | util-linux_VER ?= 2.37.4 94 | expat_VER ?= 2.5.0 95 | mpdecimal_VER ?= 2.5.1 96 | 97 | ifeq ($(USE_EXTERNAL_EXPAT),y) 98 | _ADDITIONAL_PY_CONF_FLAGS += --with-system-expat 99 | _ADDITIONAL_PY_CFLAGS += 100 | _ADDITIONAL_EXT_MODS += expat 101 | endif 102 | 103 | ifeq ($(USE_EXTERNAL_MPDECIMAL),y) 104 | _ADDITIONAL_PY_CONF_FLAGS += --with-system-libmpdec 105 | _ADDITIONAL_PY_CFLAGS += -fwrapv # python with gcc>=10.3 doesnt compile with fwrapv (by their mistake I think) 106 | _ADDITIONAL_EXT_MODS += mpdecimal 107 | endif 108 | 109 | ifneq ($(DISABLE_NCURSES_WIDE_CHAR), y) 110 | _NCURSES_LIB =ncursesw 111 | _NCURSES_EXT_CONF_FLAGS += --enable-widec 112 | else 113 | _NCURSES_LIB =ncurses 114 | endif 115 | 116 | # TODO: Add option to use wchar in ncurses: --enable-widec 117 | 118 | _ADDITIONAL_PY_CONF_FLAGS += --with-pkg-config=no --enable-optimizations=no 119 | 120 | _combine = $(word 1, $1).$(word 2, $1) 121 | util-linux_SHORT_VER ?= $(call _combine, $(subst ., ,$(util-linux_VER))) 122 | 123 | openssl_LINK ?= https://www.openssl.org/source/ 124 | bzip2_LINK ?= https://sourceware.org/pub/bzip2/ 125 | libffi_LINK ?= https://github.com/libffi/libffi/releases/download/v$(libffi_VER)/ 126 | ncurses_LINK ?= https://ftp.gnu.org/pub/gnu/ncurses/ 127 | readline_LINK ?= https://ftp.gnu.org/gnu/readline/ 128 | gdbm_LINK ?= https://ftp.gnu.org/gnu/gdbm/ 129 | sqlite-autoconf_LINK ?= https://www.sqlite.org/2022/ 130 | Python_LINK ?= https://www.python.org/ftp/python/$(Python_VER)/ 131 | xz_LINK ?= https://tukaani.org/xz/ 132 | zlib_LINK ?= https://zlib.net/fossils/ 133 | util-linux_LINK ?= https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v$(util-linux_SHORT_VER)/ 134 | expat_LINK ?= https://github.com/libexpat/libexpat/releases/download/R_$(subst .,_,$(expat_VER))/ 135 | mpdecimal_LINK ?= https://www.bytereef.org/software/mpdecimal/releases/ 136 | 137 | #bzip2-1.0.8_tar_gz = bzip2-latest.tar.gz 138 | 139 | Python_build_dir = $(PY_BUILD_DIR) 140 | 141 | TAR_XZ_PACKAGES = Python xz util-linux expat 142 | TAR_GZ_PACKAGES = zlib openssl bzip2 libffi ncurses readline gdbm sqlite-autoconf mpdecimal 143 | 144 | $(foreach package,$(TAR_XZ_PACKAGES),$(eval $(call tar_xz_template,$(package)))) 145 | $(foreach package,$(TAR_GZ_PACKAGES),$(eval $(call tar_gz_template,$(package)))) 146 | 147 | PATH_ENVS = DESTDIR="$(BUILD_DIR_ABS)/fake_root" PATH="$$PATH:$(MY_CROSS_PATH)" 148 | 149 | download: $(foreach package,$(TAR_XZ_PACKAGES),$($(package)-$($(package)_VER)_tar_xz)) $(foreach package,$(TAR_GZ_PACKAGES),$($(package)-$($(package)_VER)_tar_gz)) 150 | 151 | $(BUILD_DIR)/ $(OUTPUT_DIR)/ $(PY_BUILD_DIR)/: 152 | mkdir -p $@ 153 | 154 | $(BUILD_DIR)/made_zlib-$(zlib_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ 155 | (set -e; \ 156 | cd $(BUILD_DIR)/$*; \ 157 | CFLAGS="$(OPT_CFLAGS)" CROSS_PREFIX=$(MY_CROSS_PREFIX) ./configure --prefix=$(BUILD_DIR_ABS)/fake_root/usr/local/; \ 158 | $(MAKE) install; \ 159 | ) 160 | touch $@ 161 | 162 | $(BUILD_DIR)/made_util-linux-$(util-linux_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ 163 | (set -e; \ 164 | cd $(BUILD_DIR)/$*; \ 165 | $(PATH_ENVS) ./configure --host=$(MY_CROSS_ARCH) --prefix=/usr/local --disable-all-programs --enable-libuuid --without-python CFLAGS="-I$(BUILD_DIR_ABS)/fake_root/usr/local/include $(OPT_CFLAGS)" LDFLAGS="-L$(BUILD_DIR_ABS)/fake_root/usr/local/lib"; \ 166 | $(PATH_ENVS) $(MAKE) install; \ 167 | ) 168 | touch $@ 169 | 170 | $(BUILD_DIR)/made_sqlite-autoconf-$(sqlite-autoconf_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ $(BUILD_DIR)/made_zlib-$(zlib_VER) 171 | (set -e; \ 172 | cd $(BUILD_DIR)/$*; \ 173 | $(PATH_ENVS) ./configure --host=$(MY_CROSS_ARCH) CFLAGS="-DSQLITE_OMIT_COMPILEOPTION_DIAGS -I$(BUILD_DIR_ABS)/fake_root/usr/local/include $(OPT_CFLAGS)" LDFLAGS="-L$(BUILD_DIR_ABS)/fake_root/usr/local/lib"; \ 174 | $(PATH_ENVS) $(MAKE) install; \ 175 | ) 176 | touch $@ 177 | 178 | $(BUILD_DIR)/made_ncurses-$(ncurses_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ 179 | (set -e; \ 180 | cd $(BUILD_DIR)/$*; \ 181 | PATH="$$PATH:$(MY_CROSS_PATH)" CFLAGS="$(OPT_CFLAGS)" LDFLAGS="-fPIC" ./configure --host=$(MY_CROSS_ARCH) INSTALL="/usr/bin/install -c --strip-program=$(MY_CROSS_PREFIX)strip" --with-shared --disable-database --disable-termcap --with-fallbacks="dumb,vt100,linux,xterm-256color,vt400,xterm,putty,xterm-16color,xterm-88color,rxvt,putty-256color,konsole,screen" --enable-ext-colors --prefix=/usr/local --disable-db-install --without-manpages --without-progs --without-tack --without-tests $(_NCURSES_EXT_CONF_FLAGS); \ 182 | PATH="$$PATH:$(MY_CROSS_PATH)" $(MAKE) all DESTDIR="$(BUILD_DIR_ABS)/fake_root"; \ 183 | PATH="$$PATH:$(MY_CROSS_PATH)" $(MAKE) install DESTDIR="$(BUILD_DIR_ABS)/fake_root"; \ 184 | ) 185 | touch $@ 186 | 187 | $(BUILD_DIR)/made_readline-$(readline_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ $(BUILD_DIR)/made_ncurses-$(ncurses_VER) 188 | (set -e; \ 189 | cd $(BUILD_DIR)/$*; \ 190 | PATH="$$PATH:$(MY_CROSS_PATH)" ./configure --host=$(MY_CROSS_ARCH) CFLAGS="-I$(BUILD_DIR_ABS)/fake_root/usr/local/include $(OPT_CFLAGS)" LDFLAGS="-L$(BUILD_DIR_ABS)/fake_root/usr/local/lib -fPIC"; \ 191 | PATH="$$PATH:$(MY_CROSS_PATH)" $(MAKE) install DESTDIR="$(BUILD_DIR_ABS)/fake_root" MFLAGS="SHLIB_LIBS=\"-L$(BUILD_DIR_ABS)/fake_root/usr/local/lib -l$(_NCURSES_LIB)\""; \ 192 | ) 193 | touch $@ 194 | 195 | $(BUILD_DIR)/made_gdbm-$(gdbm_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ 196 | (set -e; \ 197 | cd $(BUILD_DIR)/$*; \ 198 | $(PATH_ENVS) ./configure --host=$(MY_CROSS_ARCH) --enable-libgdbm-compat COMPATINCLUDEDIR=/usr/local/include/gdbm CFLAGS="-Wno-builtin-macro-redefined -U__DATE__ -U__TIME__ -I$(BUILD_DIR_ABS)/fake_root/usr/local/include $(OPT_CFLAGS)" LDFLAGS="-L$(BUILD_DIR_ABS)/fake_root/usr/local/lib"; \ 199 | sed -i 's/hardcode_into_libs=yes/hardcode_into_libs=no/g' ./libtool; \ 200 | sed -i 's/hardcode_automatic=no/hardcode_automatic=yes/g' ./libtool; \ 201 | $(PATH_ENVS) $(MAKE) install; \ 202 | ) 203 | touch $@ 204 | 205 | $(BUILD_DIR)/made_xz-$(xz_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ 206 | (set -e; \ 207 | cd $(BUILD_DIR)/$*; \ 208 | CFLAGS="$(OPT_CFLAGS)" PATH="$$PATH:$(MY_CROSS_PATH)" ./configure --host=$(MY_CROSS_ARCH) --prefix="$(BUILD_DIR_ABS)/fake_root/usr/local"; \ 209 | PATH="$$PATH:$(MY_CROSS_PATH)" $(MAKE) install; \ 210 | ) 211 | touch $@ 212 | 213 | $(BUILD_DIR)/made_expat-$(expat_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ 214 | (set -e; \ 215 | cd $(BUILD_DIR)/$*; \ 216 | $(PATH_ENVS) CFLAGS="$(OPT_CFLAGS)" ./configure --host=$(MY_CROSS_ARCH); \ 217 | $(PATH_ENVS) $(MAKE) install; \ 218 | ) 219 | touch $@ 220 | 221 | $(BUILD_DIR)/made_mpdecimal-$(mpdecimal_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ 222 | (set -e; \ 223 | cd $(BUILD_DIR)/$*; \ 224 | $(PATH_ENVS) CFLAGS="$(OPT_CFLAGS) -fwrapv" ./configure --host=$(MY_CROSS_ARCH); \ 225 | $(PATH_ENVS) $(MAKE) LDFLAGS="-fPIC" install; \ 226 | ) 227 | touch $@ 228 | 229 | $(BUILD_DIR)/made_bzip2-$(bzip2_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ 230 | (set -e; \ 231 | cd $(BUILD_DIR)/$*; \ 232 | echo "" >> Makefile; \ 233 | echo "" >> Makefile; \ 234 | echo "CFLAGS += $(OPT_CFLAGS)" >> Makefile; \ 235 | echo "" >> Makefile; \ 236 | $(MAKE) install PREFIX=$(BUILD_DIR_ABS)/fake_root/usr/local CC=$(MY_CROSS_PREFIX)gcc AR=$(MY_CROSS_PREFIX)ar RANLIB=$(MY_CROSS_PREFIX)ranlib; \ 237 | $(MAKE) clean; \ 238 | $(MAKE) -f Makefile-libbz2_so all CC=$(MY_CROSS_PREFIX)gcc AR=$(MY_CROSS_PREFIX)ar RANLIB=$(MY_CROSS_PREFIX)ranlib; \ 239 | cp -d libbz2.so* $(BUILD_DIR_ABS)/fake_root/usr/local/lib; \ 240 | ln -s libbz2.so.?.?.? $(BUILD_DIR_ABS)/fake_root/usr/local/lib/libbz2.so; \ 241 | ) 242 | touch $@ 243 | 244 | $(BUILD_DIR)/made_libffi-$(libffi_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ 245 | (set -e; \ 246 | cd $(BUILD_DIR)/$*; \ 247 | CFLAGS="$(OPT_CFLAGS)" PATH="$$PATH:$(MY_CROSS_PATH)" ./configure --host=$(MY_CROSS_ARCH) --prefix=$(BUILD_DIR_ABS)/fake_root/usr/local --disable-multi-os-directory; \ 248 | PATH="$$PATH:$(MY_CROSS_PATH)" $(MAKE) install; \ 249 | ) 250 | touch $@ 251 | 252 | $(BUILD_DIR)/made_openssl-$(openssl_VER): $(BUILD_DIR)/made_%: $(BUILD_DIR)/%/ 253 | (set -e; \ 254 | cd $(BUILD_DIR)/$*; \ 255 | CFLAGS="$(OPT_CFLAGS)" CROSS_COMPILE=$(MY_CROSS_PREFIX) CC=gcc MACHINE=$(MY_CROSS_OPENSSL_MACHINE) RELEASE=5.1 SYSTEM=Linux BUILD=build ./config; \ 256 | echo "" > crypto/buildinf.h; \ 257 | echo "#define PLATFORM \"platform: \"" >> crypto/buildinf.h; \ 258 | echo "#define DATE \"built on: \"" >> crypto/buildinf.h; \ 259 | echo "static const char compiler_flags[] = \"compiler: \";" >> crypto/buildinf.h; \ 260 | echo "" >> crypto/buildinf.h; \ 261 | $(MAKE) all DESTDIR=$(BUILD_DIR_ABS)/fake_root; \ 262 | $(MAKE) install_sw DESTDIR=$(BUILD_DIR_ABS)/fake_root; \ 263 | ) 264 | touch $@ 265 | 266 | 267 | 268 | $(PY_BUILD_DIR)/made_host_Python-$(Python_VER): $(PY_BUILD_DIR)/made_host_%: %.tar.xz | $(PY_BUILD_DIR)/ 269 | (set -e; \ 270 | mkdir -p $(PY_BUILD_DIR_ABS)/host/; \ 271 | cd $(PY_BUILD_DIR_ABS)/host/; \ 272 | tar -xJf $(SRC_PATH_ABS)/$^; \ 273 | cd $*; \ 274 | ./configure; \ 275 | DESTDIR=$(PY_BUILD_DIR_ABS)/pyfakeroot/ $(MAKE) altinstall bininstall; \ 276 | ) 277 | touch $@ 278 | 279 | $(PY_BUILD_DIR)/modules_to_add: Python-$(Python_VER).tar.xz get_setup_modules.py $(PY_BUILD_DIR)/made_host_Python-$(Python_VER) $(foreach package, openssl bzip2 libffi ncurses readline gdbm sqlite-autoconf xz zlib util-linux $(_ADDITIONAL_EXT_MODS), $(BUILD_DIR)/made_$(package)-$($(package)_VER)) 280 | (set -e; \ 281 | mkdir -p $(PY_BUILD_DIR_ABS)/dyn/; \ 282 | cd $(PY_BUILD_DIR_ABS)/dyn/; \ 283 | tar -xJf $(SRC_PATH_ABS)/Python-$(Python_VER).tar.xz; \ 284 | cd Python-*; \ 285 | LDFLAGS="-L$(BUILD_DIR_ABS)/fake_root/usr/local/lib -Wl,-rpath-link,$(BUILD_DIR_ABS)/fake_root/usr/local/lib" PATH="$(PY_BUILD_DIR_ABS)/pyfakeroot/usr/local/bin:$$PATH:$(MY_CROSS_PATH)" ./configure --host=$(MY_CROSS_ARCH) --build=x86_64-pc-linux-gnu --enable-ipv6 --with-system-ffi $(_ADDITIONAL_PY_CONF_FLAGS) --with-dbmliborder=gdbm --with-ensurepip=no --with-build-python=yes --with-openssl=$(BUILD_DIR_ABS)/fake_root/usr/local ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no LIBFFI_INCLUDEDIR=$(BUILD_DIR_ABS)/fake_root/usr/local/include CPPFLAGS="-I$(BUILD_DIR_ABS)/fake_root/usr/local/include -I$(BUILD_DIR_ABS)/fake_root/usr/local/include/$(_NCURSES_LIB) -I$(BUILD_DIR_ABS)/fake_root/usr/local/include/uuid"; \ 286 | [ ! -f setup.py ] || (mv setup.py setup2.py && cp $(SRC_PATH_ABS)/get_setup_modules.py setup.py); \ 287 | MODULE_BLACKLIST="$(MODULE_BLACKLIST)" DESTDIR=$(PY_BUILD_DIR_ABS)/pyfakeroot/ PATH="$(PY_BUILD_DIR_ABS)/pyfakeroot/usr/local/bin:$$PATH:$(MY_CROSS_PATH)" $(MAKE) sharedmods; \ 288 | [ -f modules_to_add ] || (echo "*disabled*" > modules_to_add && echo "$(MODULE_BLACKLIST)" >> modules_to_add); \ 289 | cp modules_to_add $(SRC_PATH_ABS)/$@; \ 290 | ) 291 | 292 | $(PY_BUILD_DIR)/made_Python-$(Python_VER): $(PY_BUILD_DIR)/made_%: $(PY_BUILD_DIR)/%/ $(PY_BUILD_DIR)/modules_to_add 293 | (set -e; \ 294 | cd $(PY_BUILD_DIR)/$*; \ 295 | rm -f Modules/Setup.local; \ 296 | cp $(PY_BUILD_DIR_ABS)/modules_to_add Modules/Setup.local; \ 297 | [ ! -f Modules/Setup ] || for mod in $(MODULE_BLACKLIST); do sed -i "s/^$$mod\\s.*//g" Modules/Setup; done; \ 298 | [ ! -f Modules/Setup.bootstrap.in ] || for mod in $(MODULE_BLACKLIST); do sed -i "s/^$$mod\\s.*//g" Modules/Setup.bootstrap.in; done; \ 299 | [ ! -f Modules/Setup.stdlib.in ] || sed -i 's/^\*shared\*$$/*disabled*/g' Modules/Setup.stdlib.in; \ 300 | \ 301 | EXTRA_CONFIG_FLAGS=$$(CROSS_PREFIX="$(MY_CROSS_PREFIX)" PYTHONHOME="$(PY_BUILD_DIR_ABS)"/pyfakeroot/usr/local "$(PY_BUILD_DIR_ABS)"/pyfakeroot/usr/local/bin/python[0-9] $(SRC_PATH_ABS)/configure_cross_flags.py); \ 302 | \ 303 | MODULE_BUILDTYPE=static LINKFORSHARED=" " CCSHARED=" " LDFLAGS="-L$(BUILD_DIR_ABS)/fake_root/usr/local/lib -Wl,-rpath-link,$(BUILD_DIR_ABS)/fake_root/usr/local/lib" PATH="$(PY_BUILD_DIR_ABS)/pyfakeroot/usr/local/bin:$$PATH:$(MY_CROSS_PATH)" ./configure --host=$(MY_CROSS_ARCH) --build=x86_64-pc-linux-gnu --enable-ipv6 --enable-optimizations --with-lto --with-system-ffi $(_ADDITIONAL_PY_CONF_FLAGS) --with-dbmliborder=gdbm --with-ensurepip=no --disable-shared --with-tzpath="" --with-build-python=yes --with-openssl=$(BUILD_DIR_ABS)/fake_root/usr/local ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no LIBFFI_INCLUDEDIR=$(BUILD_DIR_ABS)/fake_root/usr/local/include CPPFLAGS="-I$(BUILD_DIR_ABS)/fake_root/usr/local/include -I$(BUILD_DIR_ABS)/fake_root/usr/local/include/$(_NCURSES_LIB) -I$(BUILD_DIR_ABS)/fake_root/usr/local/include/uuid" $$EXTRA_CONFIG_FLAGS; \ 304 | \ 305 | echo "" >> Modules/errnomodule.c; \ 306 | echo "" >> Modules/errnomodule.c; \ 307 | echo "#include " >> Modules/errnomodule.c; \ 308 | echo "" >> Modules/errnomodule.c; \ 309 | echo "static void __attribute__((constructor)) my_pythonhome_ctor(void) {" >> Modules/errnomodule.c; \ 310 | echo " const char* pythonhome_env = getenv(\"PYTHONHOME\");" >> Modules/errnomodule.c; \ 311 | echo " if (pythonhome_env == NULL || pythonhome_env[0] == '\\0') {" >> Modules/errnomodule.c; \ 312 | echo "#if PY_VERSION_HEX >= 0x030000a5" >> Modules/errnomodule.c; \ 313 | echo "#define PYTHON_WCHAR(str) L##str" >> Modules/errnomodule.c; \ 314 | echo "#else" >> Modules/errnomodule.c; \ 315 | echo "#define PYTHON_WCHAR(str) str" >> Modules/errnomodule.c; \ 316 | echo "#endif" >> Modules/errnomodule.c; \ 317 | echo " Py_SetPythonHome(PYTHON_WCHAR(\"$(FINAL_PYTHONHOME_PATH)\"));" >> Modules/errnomodule.c; \ 318 | echo " }" >> Modules/errnomodule.c; \ 319 | echo "}" >> Modules/errnomodule.c; \ 320 | echo "" >> Modules/errnomodule.c; \ 321 | echo "" >> Modules/errnomodule.c; \ 322 | if [ -f ./Lib/plat-generic/regen ]; then \ 323 | echo "#!/bin/sh" > ./Lib/plat-generic/regen; \ 324 | echo "" >> ./Lib/plat-generic/regen; \ 325 | fi; \ 326 | mkdir $(PY_BUILD_DIR_ABS)/pyfakeroot2/ || true; \ 327 | echo "" >> Makefile; \ 328 | echo "LDFLAGS += -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -static $(OPT_LDFLAGS) $(_ADDITIONAL_PY_LDFLAGS)" >> Makefile; \ 329 | echo "" >> Makefile; \ 330 | DESTDIR=$(PY_BUILD_DIR_ABS)/pyfakeroot2/ PATH="$(PY_BUILD_DIR_ABS)/pyfakeroot/usr/local/bin:$$PATH:$(MY_CROSS_PATH)" EXTRA_CFLAGS="-DCOMPILER=\\\"[C]\\\" -DDATE=\\\"xx_xx_xx\\\" -Wno-builtin-macro-redefined -U__DATE__ -U__TIME__ $(OPT_CFLAGS) $(_ADDITIONAL_PY_CFLAGS)" $(MAKE) libinstall SOABI="cpython-" MULTIARCH="" MULTIARCH_CPPFLAGS="" ; \ 331 | ) 332 | touch $@ 333 | 334 | $(PY_BUILD_DIR)/python-stripped: $(PY_BUILD_DIR)/made_Python-$(Python_VER) 335 | $(MY_CROSS_PREFIX)objcopy -R .comment -R '.comment.*' -R .note -R '.note.*' -S $(PY_BUILD_DIR)/Python-$(Python_VER)/python $(SRC_PATH_ABS)/$@ 336 | 337 | $(PY_BUILD_DIR)/python_lib.zip: $(PY_BUILD_DIR)/made_Python-$(Python_VER) zipper.py 338 | (set -e; cd $(PY_BUILD_DIR_ABS)/pyfakeroot2/; make -f $(PY_BUILD_DIR_ABS)/Python-$(Python_VER)/Makefile pycremoval SOABI="cpython-" MULTIARCH="" MULTIARCH_CPPFLAGS="") 339 | (set -e; \ 340 | cd $(PY_BUILD_DIR_ABS)/pyfakeroot2/usr/local/lib*/*; \ 341 | $(DO_ON_RELEASE) rm -r test/ || true; \ 342 | $(DO_ON_RELEASE) rm -r lib2to3/tests/ || true; \ 343 | $(DO_ON_RELEASE) rm -r unittest/test/ || true; \ 344 | $(DO_ON_RELEASE) rm -r ctypes/test/ || true; \ 345 | $(DO_ON_RELEASE) rm -r distutils/tests/ || true; \ 346 | $(DO_ON_RELEASE) rm -r tkinter/test/ || true; \ 347 | $(DO_ON_RELEASE) rm -r idlelib/idle_test/ || true; \ 348 | $(DO_ON_RELEASE) rm -r sqlite3/test/ || true; \ 349 | $(DO_ON_RELEASE) rm -r ensurepip/ || true; \ 350 | $(DO_ON_RELEASE) rm -r email/test || true; \ 351 | $(DO_ON_RELEASE) rm -r json/tests || true; \ 352 | $(DO_ON_RELEASE) rm -r bsddb/test || true; \ 353 | $(DO_ON_RELEASE) rm -r lib-tk/test || true; \ 354 | $(DO_ON_RELEASE) rm lib2to3/*Grammar*.pickle || true; \ 355 | if [ -f _sysconfigdata*.py ]; then \ 356 | PYTHONHOME=$(PY_BUILD_DIR_ABS)/pyfakeroot/usr/local $(PY_BUILD_DIR_ABS)/pyfakeroot/usr/local/bin/python[0-9] $(SRC_PATH_ABS)/sysconfig_filter.py; \ 357 | fi; \ 358 | ) 359 | # fixup ctypes to load 360 | sed -i 's/pythonapi = PyDLL(None)/pythonapi = None/g' $(PY_BUILD_DIR_ABS)/pyfakeroot2/usr/local/lib*/*/ctypes/__init__.py || true 361 | PYTHONHOME=$(PY_BUILD_DIR_ABS)/pyfakeroot/usr/local $(PY_BUILD_DIR_ABS)/pyfakeroot/usr/local/bin/python[0-9] $(SRC_PATH_ABS)/zipper.py $(PY_BUILD_DIR_ABS)/pyfakeroot2/usr/local $(SRC_PATH_ABS)/$@ 362 | 363 | $(PY_BUILD_DIR)/static_python: $(PY_BUILD_DIR)/python-stripped $(PY_BUILD_DIR)/python_lib.zip 364 | cat $(PY_BUILD_DIR)/python-stripped $(PY_BUILD_DIR)/python_lib.zip > $@ 365 | chmod u+x $@ 366 | 367 | $(OUTPUT_DIR)/static_python: $(PY_BUILD_DIR)/static_python | $(OUTPUT_DIR)/ 368 | cp $^ $@ 369 | 370 | 371 | 372 | all: $(OUTPUT_DIR)/static_python 373 | 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # static-python 2 | 3 | [![Automatic ci](https://github.com/sagimor6/static-python/actions/workflows/automatic.yml/badge.svg?branch=master&event=push)](https://github.com/sagimor6/static-python/actions/workflows/automatic.yml) 4 | 5 | This script is supposed to cross compile a statically linked python 3 or 2. 6 | The result is a single file that contains python and all its builtin modules. 7 | 8 | My goal is that the build will be reproducible and deterministic 9 | (if two people compile using this script with the same toolchain and module versions, the results should be the same). 10 | 11 | Currently, the script minimizes size in exchange for performance. 12 | 13 | The script was tested with python 3.9.7 and 2.7.18 for aarch64 and worked. 14 | 15 | USAGE: 16 | ``` 17 | make distclean 18 | MY_CROSS_ARCH=aarch64-buildroot-linux-musl MY_CROSS_PATH=/opt/aarch64-buildroot-linux-musl/bin Python_VER=3.9.7 make -j all 19 | ``` 20 | 21 | In order to blacklist modules use the ``MODULE_BLACKLIST`` environment variable, which is a list of module names seperated by spaces. 22 | for example to build a very minimal static python: 23 | ``` 24 | export MODULE_BLACKLIST="gdbm dbm crypt _xxsubinterpreters audioop _testcapi _testinternalcapi _testbuffer _testimportmultiple _testmultiphase _xxtestfuzz readline _curses _curses_panel _crypt _ssl _hashlib _dbm _gdbm _sqlite3 ossaudiodev _bz2 _lzma pyexpat _elementtree _multibytecodec _codecs_kr _codecs_jp _codecs_cn _codecs_tw _codecs_hk _codecs_iso2022 _decimal _ctypes_test _ctypes _uuid xxlimited" 25 | make -j all 26 | ``` 27 | 28 | When building the Dockerfile, you need to pass ARCH and LIBC args. for exmaple: 29 | ``` 30 | docker build -t static_python_gen --build-arg=ARCH=aarch64 --build-arg=LIBC=musl . 31 | ``` 32 | 33 | TODO: 34 | - I currently don't support the tkinter module. 35 | - There are no tests. 36 | - Don't compile dependencies of blacklisted modules. 37 | - Enable option to remove non binary modules, like distutils. 38 | - more TODOs. 39 | 40 | -------------------------------------------------------------------------------- /bootlin_toolchain_downloader.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import subprocess 4 | import re 5 | import requests 6 | import argparse 7 | import stat 8 | 9 | def main(): 10 | parser = argparse.ArgumentParser(description='Process some integers.') 11 | parser.add_argument('arch', help='The architecture. E.g: aarch64, x86-64, ...') 12 | parser.add_argument('libc', help='The libc. E.g: musl, glibc, ...') 13 | parser.add_argument('--date', help='The toolchain date. E.g: 2021.11, 2020.08, ...') 14 | parser.add_argument('--bleeding', action='store_true', default=False, help='Use bleeding edge toolchain, not the stable one') 15 | parser.add_argument('--extract', action='store_true', default=False, help='Exctact toolchain') 16 | parser.add_argument('--make_runner', action='store_true', default=False, help='Make Makefile runner') 17 | 18 | args = parser.parse_args() 19 | 20 | url = 'https://toolchains.bootlin.com/downloads/releases/toolchains/{}/tarballs/'.format(args.arch) 21 | 22 | body = requests.get(url).text 23 | 24 | matcher = re.compile(r'\(?P\w(\w|\-\w)*)\-\-(?P\w+)\-\-(?P(stable|bleeding\-edge))\-(?P[0-9]{4}\.[0-9]{2})\-(?P[0-9]+)\.tar\.bz2)\"\>') 25 | 26 | matches = [] 27 | 28 | best_match = None 29 | 30 | for match in matcher.finditer(body): 31 | match = match.groupdict() 32 | if match['arch'] != args.arch: 33 | continue 34 | 35 | if match['libc'] != args.libc: 36 | continue 37 | 38 | if args.date is not None: 39 | if match['date'] != args.date and match['date'] + '-' + match['num'] != args.date: 40 | continue 41 | 42 | if args.bleeding: 43 | if match['stability'] != 'bleeding-edge': 44 | continue 45 | else: 46 | if match['stability'] != 'stable': 47 | continue 48 | 49 | if best_match is None or (best_match['date'] < match['date'] or (best_match['date'] == match['date'] and int(best_match['num'], 10) < int(match['num'], 10))): 50 | best_match = match 51 | 52 | assert best_match is not None 53 | 54 | filename = best_match['url'] # filename is ok according to regex, no path traversal 55 | url = 'https://toolchains.bootlin.com/downloads/releases/toolchains/{}/tarballs/{}'.format(args.arch, filename) 56 | 57 | print('downloading from {}'.format(url)) 58 | 59 | content = requests.get(url).content 60 | with open(filename, 'wb') as f: 61 | f.write(content) 62 | 63 | if args.extract: 64 | subprocess.call(['tar', '-xjf', filename]) 65 | 66 | dir_name = filename[:-len('.tar.bz2')] 67 | 68 | gcc = None 69 | for fname in os.listdir(os.path.join(dir_name, 'bin')): 70 | if fname.endswith('-gcc'): 71 | if gcc is None or len(gcc) < len(fname): 72 | gcc = fname 73 | 74 | if gcc is not None: 75 | prefix = gcc[:-len('-gcc')] 76 | print('compile with: MY_CROSS_ARCH="{}" MY_CROSS_PATH="{}"'.format(prefix, os.path.join(os.getcwd(), dir_name, 'bin'))) 77 | if args.make_runner: 78 | with open('make_runner.sh', 'w') as f: 79 | f.write('''#!/bin/sh 80 | MY_CROSS_ARCH="{}" MY_CROSS_PATH="{}" make "$@" 81 | '''.format(prefix, os.path.join(os.getcwd(), dir_name, 'bin'))) 82 | os.chmod('make_runner.sh', stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH) 83 | 84 | 85 | if __name__ == '__main__': 86 | main() 87 | -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | MY_CROSS_PREFIX=/opt/aarch64-buildroot-linux-musl/bin/aarch64-buildroot-linux-musl- 4 | MY_CROSS_PATH=/opt/aarch64-buildroot-linux-musl/bin 5 | MY_CROSS_ARCH=aarch64-buildroot-linux-musl 6 | MY_CROSS_ARCH2=linux-aarch64 7 | MY_CROSS_ARCH3=aarch64 8 | 9 | OPT_CFLAGS="-ffunction-sections -fdata-sections -flto -fuse-linker-plugin -ffat-lto-objects -Os" 10 | OPT_LDFLAGS="-flto -fuse-linker-plugin -ffat-lto-objects -Wl,--gc-sections -Os -flto-partition=one" 11 | 12 | 13 | SRC_PATH=$(pwd) 14 | 15 | 16 | rm -rf final 17 | mkdir final 18 | 19 | rm -rf build 20 | mkdir build 21 | 22 | cd build 23 | 24 | BUILD_DIR=$(pwd) 25 | 26 | tar -xJf $SRC_PATH/Python-3.9.7.tar.xz 27 | #tar -xJf $SRC_PATH/Python-2.7.18.tar.xz 28 | tar -xJf $SRC_PATH/xz-5.2.5.tar.xz 29 | tar -xJf $SRC_PATH/zlib-1.2.11.tar.xz 30 | tar -xzf $SRC_PATH/openssl-1.1.1l.tar.gz 31 | tar -xzf $SRC_PATH/bzip2-latest.tar.gz 32 | tar -xzf $SRC_PATH/libffi-3.4.2.tar.gz 33 | tar -xzf $SRC_PATH/ncurses-6.2.tar.gz 34 | tar -xzf $SRC_PATH/readline-8.1.tar.gz 35 | tar -xzf $SRC_PATH/gdbm-1.23.tar.gz 36 | tar -xzf $SRC_PATH/sqlite-autoconf-3380100.tar.gz 37 | tar -xJf $SRC_PATH/util-linux-2.37.4.tar.xz 38 | # tar -xzf $SRC_PATH/tcl8.6.12-src.tar.gz 39 | # tar -xzf $SRC_PATH/tk8.6.12-src.tar.gz 40 | 41 | mkdir fake_root 42 | mkdir pyfakeroot 43 | 44 | cd zlib-1.2.11/ 45 | make distclean 46 | 47 | CFLAGS="${OPT_CFLAGS}" CROSS_PREFIX=${MY_CROSS_PREFIX} ./configure --prefix=${BUILD_DIR}/fake_root/usr/local/ 48 | 49 | make clean 50 | make all -j8 51 | make install 52 | 53 | cd .. 54 | 55 | cd util-linux-2.37.4 56 | 57 | PATH="$PATH:${MY_CROSS_PATH}" ./configure --host=$MY_CROSS_ARCH --prefix=/usr/local --disable-all-programs --enable-libuuid --without-python CFLAGS="-I${BUILD_DIR}/fake_root/usr/local/include ${OPT_CFLAGS}" LDFLAGS="-L${BUILD_DIR}/fake_root/usr/local/lib" 58 | 59 | DESTDIR="${BUILD_DIR}/fake_root" PATH="$PATH:${MY_CROSS_PATH}" make -j8 all 60 | DESTDIR="${BUILD_DIR}/fake_root" PATH="$PATH:${MY_CROSS_PATH}" make install 61 | 62 | cd .. 63 | 64 | # cd tcl8.6.12/unix 65 | # 66 | # PATH="$PATH:${MY_CROSS_PATH}" ./configure --host=$MY_CROSS_ARCH --with-system-sqlite --without-tzdata CFLAGS="-I${BUILD_DIR}/fake_root/usr/local/include ${OPT_CFLAGS}" LDFLAGS="-L${BUILD_DIR}/fake_root/usr/local/lib" 67 | # 68 | # DESTDIR="${BUILD_DIR}/fake_root" PATH="$PATH:${MY_CROSS_PATH}" make -j8 install-binaries 69 | # 70 | # cd ../.. 71 | # 72 | # cd tk8.6.12/unix 73 | # 74 | # PATH="$PATH:${MY_CROSS_PATH}" ./configure --host=$MY_CROSS_ARCH --with-tcl="${BUILD_DIR}/fake_root/usr/local/lib" CFLAGS="-I${BUILD_DIR}/fake_root/usr/local/include ${OPT_CFLAGS}" LDFLAGS="-L${BUILD_DIR}/fake_root/usr/local/lib" 75 | # 76 | # DESTDIR="${BUILD_DIR}/fake_root" PATH="$PATH:${MY_CROSS_PATH}" make -j8 install-binaries 77 | # 78 | # cd ../../ 79 | # 80 | # cd tcl8.6.12/unix 81 | # 82 | # make distclean 83 | # 84 | # PATH="$PATH:${MY_CROSS_PATH}" ./configure --host=$MY_CROSS_ARCH --disable-shared --with-system-sqlite --without-tzdata CFLAGS="-I${BUILD_DIR}/fake_root/usr/local/include ${OPT_CFLAGS}" LDFLAGS="-L${BUILD_DIR}/fake_root/usr/local/lib" 85 | # 86 | # DESTDIR="${BUILD_DIR}/fake_root" PATH="$PATH:${MY_CROSS_PATH}" make -j8 install-binaries install-headers 87 | # 88 | # cd ../.. 89 | # 90 | # cd tk8.6.12/unix 91 | # 92 | # make distclean 93 | # 94 | # PATH="$PATH:${MY_CROSS_PATH}" ./configure --host=$MY_CROSS_ARCH --disable-shared --with-tcl="{BUILD_DIR}/fake_root/usr/local/lib" CFLAGS="-I${BUILD_DIR}/fake_root/usr/local/include ${OPT_CFLAGS}" LDFLAGS="-L${BUILD_DIR}/fake_root/usr/local/lib" 95 | # 96 | # DESTDIR="${BUILD_DIR}/fake_root" PATH="$PATH:${MY_CROSS_PATH}" make -j8 install-binaries install-headers 97 | # 98 | # cd ../.. 99 | 100 | cd sqlite-autoconf-3380100 101 | 102 | PATH="$PATH:${MY_CROSS_PATH}" ./configure --host=$MY_CROSS_ARCH CFLAGS="-DSQLITE_OMIT_COMPILEOPTION_DIAGS -I${BUILD_DIR}/fake_root/usr/local/include ${OPT_CFLAGS}" LDFLAGS="-L${BUILD_DIR}/fake_root/usr/local/lib" 103 | 104 | DESTDIR="${BUILD_DIR}/fake_root" PATH="$PATH:${MY_CROSS_PATH}" make -j8 all 105 | DESTDIR="${BUILD_DIR}/fake_root" PATH="$PATH:${MY_CROSS_PATH}" make install 106 | 107 | cd .. 108 | 109 | cd ncurses-6.2/ 110 | 111 | PATH="$PATH:${MY_CROSS_PATH}" CFLAGS="${OPT_CFLAGS}" ./configure --host=$MY_CROSS_ARCH INSTALL="/usr/bin/install -c --strip-program=${MY_CROSS_PREFIX}strip" --with-shared --disable-database --disable-termcap --with-fallbacks="dumb,vt100,linux,xterm-256color,vt400,xterm,putty,xterm-16color,xterm-88color,rxvt,putty-256color,konsole,screen" --prefix=/usr/local --disable-db-install --without-manpages --without-progs --without-tack --without-tests 112 | PATH="$PATH:${MY_CROSS_PATH}" make -j8 all DESTDIR="${BUILD_DIR}/fake_root" 113 | PATH="$PATH:${MY_CROSS_PATH}" make install DESTDIR="${BUILD_DIR}/fake_root" 114 | 115 | cd .. 116 | 117 | cd readline-8.1 118 | 119 | PATH="$PATH:${MY_CROSS_PATH}" ./configure --host=$MY_CROSS_ARCH CFLAGS="-I${BUILD_DIR}/fake_root/usr/local/include ${OPT_CFLAGS}" LDFLAGS="-L${BUILD_DIR}/fake_root/usr/local/lib" 120 | PATH="$PATH:${MY_CROSS_PATH}" make -j8 all DESTDIR="${BUILD_DIR}/fake_root" MFLAGS="SHLIB_LIBS=\"-L${BUILD_DIR}/fake_root/usr/local/lib -lncurses\"" 121 | PATH="$PATH:${MY_CROSS_PATH}" make install DESTDIR="${BUILD_DIR}/fake_root" 122 | 123 | cd .. 124 | 125 | cd gdbm-1.23/ 126 | 127 | PATH="$PATH:${MY_CROSS_PATH}" ./configure --host=$MY_CROSS_ARCH --enable-libgdbm-compat CFLAGS="-Wno-builtin-macro-redefined -U__DATE__ -U__TIME__ -I${BUILD_DIR}/fake_root/usr/local/include ${OPT_CFLAGS}" LDFLAGS="-L${BUILD_DIR}/fake_root/usr/local/lib" 128 | 129 | DESTDIR="${BUILD_DIR}/fake_root" PATH="$PATH:${MY_CROSS_PATH}" make -j8 all 130 | DESTDIR="${BUILD_DIR}/fake_root" PATH="$PATH:${MY_CROSS_PATH}" make install 131 | 132 | cd .. 133 | 134 | cd xz-5.2.5/ 135 | 136 | CFLAGS="${OPT_CFLAGS}" PATH="$PATH:${MY_CROSS_PATH}" ./configure --host=${MY_CROSS_ARCH} --prefix="${BUILD_DIR}/fake_root/usr/local" 137 | 138 | PATH="$PATH:${MY_CROSS_PATH}" make clean 139 | 140 | PATH="$PATH:${MY_CROSS_PATH}" make all -j8 141 | 142 | PATH="$PATH:${MY_CROSS_PATH}" make install 143 | 144 | cd .. 145 | 146 | cd bzip2-1.0.8/ 147 | 148 | make clean 149 | 150 | echo " 151 | 152 | CFLAGS += ${OPT_CFLAGS} 153 | 154 | " >> Makefile 155 | 156 | make -j8 bzip2 CC=${MY_CROSS_PREFIX}gcc AR=${MY_CROSS_PREFIX}ar RANLIB=${MY_CROSS_PREFIX}ranlib 157 | 158 | make install PREFIX=${BUILD_DIR}/fake_root/usr/local CC=${MY_CROSS_PREFIX}gcc AR=${MY_CROSS_PREFIX}ar RANLIB=${MY_CROSS_PREFIX}ranlib 159 | 160 | make clean 161 | 162 | make -f Makefile-libbz2_so -j8 all CC=${MY_CROSS_PREFIX}gcc AR=${MY_CROSS_PREFIX}ar RANLIB=${MY_CROSS_PREFIX}ranlib 163 | 164 | cp -d libbz2.so* ${BUILD_DIR}/fake_root/usr/local/lib 165 | ln -s libbz2.so.?.?.? ${BUILD_DIR}/fake_root/usr/local/lib/libbz2.so 166 | 167 | cd .. 168 | 169 | cd libffi-3.4.2/ 170 | 171 | CFLAGS="${OPT_CFLAGS}" PATH="$PATH:${MY_CROSS_PATH}" ./configure --host=${MY_CROSS_ARCH} --prefix=${BUILD_DIR}/fake_root/usr/local --disable-multi-os-directory 172 | 173 | PATH="$PATH:${MY_CROSS_PATH}" make all -j8 174 | 175 | PATH="$PATH:${MY_CROSS_PATH}" make install 176 | 177 | cd .. 178 | 179 | cd openssl-1.1.1l/ 180 | 181 | CFLAGS="${OPT_CFLAGS}" CROSS_COMPILE=${MY_CROSS_PREFIX} MACHINE=${MY_CROSS_ARCH3} RELEASE=5.1 SYSTEM=Linux BUILD=build ./config 182 | #CFLAGS="${OPT_CFLAGS}" CROSS_COMPILE=${MY_CROSS_PREFIX} ./Configure ${MY_CROSS_ARCH2} 183 | 184 | echo " 185 | #define PLATFORM \"platform: \" 186 | #define DATE \"built on: \" 187 | static const char compiler_flags[] = \"compiler: \"; 188 | " > crypto/buildinf.h 189 | 190 | make all -j8 DESTDIR=${BUILD_DIR}/fake_root 191 | 192 | make install_sw -j8 DESTDIR=${BUILD_DIR}/fake_root 193 | 194 | cd .. 195 | 196 | cd Python-3.9.7/ 197 | #cd Python-2.7.18/ 198 | 199 | ./configure 200 | 201 | DESTDIR=${BUILD_DIR}/pyfakeroot/ make -j8 altinstall 202 | 203 | make distclean 204 | 205 | export PKG_CONFIG=pkg-config 206 | export PKG_CONFIG_LIBDIR=${BUILD_DIR}/fake_root/usr/local/lib/pkgconfig 207 | 208 | LDFLAGS="-L${BUILD_DIR}/fake_root/usr/local/lib" PATH="${BUILD_DIR}/pyfakeroot/usr/local/bin:$PATH:${MY_CROSS_PATH}" ./configure --host=${MY_CROSS_ARCH} --build=x86_64-pc-linux-gnu --enable-ipv6 --with-system-ffi --with-ensurepip=no --with-openssl=${BUILD_DIR}/fake_root/usr/local ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no LIBFFI_INCLUDEDIR=${BUILD_DIR}/fake_root/usr/local/include CPPFLAGS="-I${BUILD_DIR}/fake_root/usr/local/include -I${BUILD_DIR}/fake_root/usr/local/include/ncurses -I${BUILD_DIR}/fake_root/usr/local/include/uuid" 209 | 210 | # echo " 211 | # *shared* 212 | # 213 | # _testcapi 214 | # 215 | # 216 | # 217 | # 218 | # " >> Modules/Setup.local 219 | 220 | mv setup.py setup2.py 221 | cp $SRC_PATH/get_setup_modules.py setup.py 222 | DESTDIR=${BUILD_DIR}/pyfakeroot/ PATH="${BUILD_DIR}/pyfakeroot/usr/local/bin:$PATH:${MY_CROSS_PATH}" make sharedmods -j8 223 | rm -f setup.py 224 | mv setup2.py setup.py 225 | 226 | make distclean || true 227 | 228 | rm -f Modules/Setup.local 229 | cp modules_to_add Modules/Setup.local 230 | 231 | LINKFORSHARED=" " LDFLAGS="-L${BUILD_DIR}/fake_root/usr/local/lib" PATH="${BUILD_DIR}/pyfakeroot/usr/local/bin:$PATH:${MY_CROSS_PATH}" ./configure --host=${MY_CROSS_ARCH} --build=x86_64-pc-linux-gnu --enable-ipv6 --enable-optimizations --with-lto --with-system-ffi --with-ensurepip=no --disable-shared --with-tzpath="" --with-openssl=${BUILD_DIR}/fake_root/usr/local ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no LIBFFI_INCLUDEDIR=${BUILD_DIR}/fake_root/usr/local/include CPPFLAGS="-I${BUILD_DIR}/fake_root/usr/local/include -I${BUILD_DIR}/fake_root/usr/local/include/ncurses -I${BUILD_DIR}/fake_root/usr/local/include/uuid" 232 | 233 | echo " 234 | 235 | #include 236 | 237 | static void __attribute__((constructor)) my_pythonhome_ctor() { 238 | if (getenv(\"PYTHONHOME\") == NULL) { 239 | putenv(\"PYTHONHOME=/proc/self/exe\"); 240 | } 241 | } 242 | 243 | " >> Modules/errnomodule.c 244 | 245 | if [ -f ./Lib/plat-generic/regen ]; then 246 | echo "#!/bin/sh 247 | " > ./Lib/plat-generic/regen 248 | fi 249 | 250 | mkdir ${BUILD_DIR}/pyfakeroot2/ || true 251 | 252 | # this is because old pythons 253 | echo " 254 | LDFLAGS += -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -static ${OPT_LDFLAGS} 255 | " >> Makefile 256 | 257 | DESTDIR=${BUILD_DIR}/pyfakeroot2/ PATH="${BUILD_DIR}/pyfakeroot/usr/local/bin:$PATH:${MY_CROSS_PATH}" EXTRA_CFLAGS="-DCOMPILER=\\\"\\\" -Wno-builtin-macro-redefined -U__DATE__ -U__TIME__ ${OPT_CFLAGS}" make libinstall -j8 258 | 259 | (_CUR_DIR=$(pwd); cd ${BUILD_DIR}/pyfakeroot2/; make -f $_CUR_DIR/Makefile pycremoval) 260 | 261 | ( 262 | cd ${BUILD_DIR}/pyfakeroot2/usr/local/*/* 263 | rm -r test/ || true 264 | rm -r lib2to3/tests/ || true 265 | rm -r unittest/test/ || true 266 | rm -r ctypes/test/ || true 267 | rm -r distutils/tests/ || true 268 | rm -r tkinter/test/ || true 269 | rm -r idlelib/idle_test/ || true 270 | rm -r sqlite3/test/ || true 271 | rm -r ensurepip/ || true 272 | rm -r email/test || true 273 | rm -r json/tests || true 274 | rm -r bsddb/test || true 275 | rm -r lib-tk/test || true 276 | ) 277 | 278 | ( 279 | cd ${BUILD_DIR}/pyfakeroot2/usr/local/*/* 280 | rm lib2to3/*Grammar*.pickle || true 281 | ) 282 | 283 | ( 284 | cd ${BUILD_DIR}/pyfakeroot2/usr/local/*/* 285 | if [ -f _sysconfigdata*.py ]; then 286 | echo "# system configuration generated and used by the sysconfig module 287 | build_time_vars = {'TZPATH': ''}" > $(echo _sysconfigdata*.py) 288 | fi 289 | ) 290 | 291 | 292 | # fixup ctypes to load 293 | sed -i 's/pythonapi = PyDLL(None)/pythonapi = None/g' ${BUILD_DIR}/pyfakeroot2/usr/local/*/*/ctypes/__init__.py || true 294 | 295 | PYTHONHOME=${BUILD_DIR}/pyfakeroot/usr/local ${BUILD_DIR}/pyfakeroot/usr/local/bin/python?.? $SRC_PATH/zipper.py ${BUILD_DIR}/pyfakeroot2/usr/local ../python_lib.zip 296 | 297 | ${MY_CROSS_PREFIX}objcopy -R .comment -R '.comment.*' -R .note -R '.note.*' -S ./python ../python-stripped 298 | 299 | cd .. 300 | 301 | cat python-stripped python_lib.zip > static_python 302 | chmod u+x ./static_python 303 | 304 | cp ./static_python ../final/static_python 305 | 306 | cd .. 307 | 308 | 309 | -------------------------------------------------------------------------------- /configure_cross_flags.py: -------------------------------------------------------------------------------- 1 | 2 | import subprocess 3 | import os 4 | import tempfile 5 | import shutil 6 | 7 | def run_cross_cmd(cmd, *flags): 8 | cross_prefix = os.environ['CROSS_PREFIX'] 9 | with open('/dev/null', 'r+b') as devnull: 10 | proc = subprocess.Popen((cross_prefix + cmd,) + flags, stdin=devnull, stdout=devnull, stderr=devnull, shell=False) 11 | return proc.wait() 12 | 13 | def main(): 14 | 15 | dir = tempfile.mkdtemp() 16 | try: 17 | with open(os.path.join(dir, 'test.c'), 'w') as f: 18 | f.write(""" 19 | 20 | double d = 90904234967036810337470478905505011476211692735615632014797120844053488865816695273723469097858056257517020191247487429516932130503560650002327564517570778480236724525140520121371739201496540132640109977779420565776568942592.0; 21 | 22 | """) 23 | 24 | assert run_cross_cmd('gcc', '-c', '-o', os.path.join(dir, 'test.o'), os.path.join(dir, 'test.c')) == 0 25 | 26 | with open(os.path.join(dir, 'test.o'), 'rb') as f: 27 | res = f.read() 28 | 29 | if b'noonsees' in res: 30 | print('ac_cv_big_endian_double=yes') 31 | elif b'seesnoon' in res: 32 | print('ac_cv_little_endian_double=yes') 33 | finally: 34 | shutil.rmtree(dir) 35 | 36 | print('ac_cv_working_tzset=yes') 37 | 38 | 39 | 40 | if __name__ == '__main__': 41 | main() 42 | -------------------------------------------------------------------------------- /get_setup_modules.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import os 4 | import sysconfig 5 | 6 | sys.path.append('.') 7 | 8 | import setup2 9 | 10 | def get_setup_build_ext(): 11 | class Dummy: 12 | pass 13 | res = Dummy() 14 | from distutils.command.build_ext import build_ext 15 | 16 | orig_sysconfig_get_config_var = sysconfig.get_config_var 17 | 18 | def sysconfig_get_config_var(name): 19 | if name == 'PYTHONFRAMEWORK': 20 | return 'a' 21 | else: 22 | return orig_sysconfig_get_config_var(name) 23 | 24 | class MyBuildExt(build_ext): 25 | def __init__(self, dist): 26 | build_ext.__init__(self, dist) 27 | 28 | def build_extensions(self): 29 | 30 | if 'MODULE_BLACKLIST' in os.environ and len(os.environ['MODULE_BLACKLIST'].strip()) != 0: 31 | module_blacklist = set(os.environ['MODULE_BLACKLIST'].strip().split(' ')) 32 | exts = [] 33 | for e in self.extensions: 34 | if e.name in module_blacklist: 35 | self.failed.append(e.name) 36 | else: 37 | exts.append(e) 38 | self.extensions = exts 39 | 40 | for e in self.extensions: 41 | e.library_dirs = [x for x in e.library_dirs if not x.startswith('/usr/lib')] 42 | 43 | build_ext.build_extensions(self) 44 | 45 | lines = [] 46 | for e in self.extensions: 47 | if e.name in self.failed: 48 | continue 49 | defines_and_flags = ' '.join(['-D' + define_to_str(x) for x in e.define_macros] + [x for x in e.extra_compile_args]) 50 | if e.name == '_testcapi': 51 | defines_and_flags += ' -UPy_BUILD_CORE_BUILTIN -UPy_BUILD_CORE ' 52 | def_line = '__' + e.name + '_DEFS=__BLABLA__ ' + defines_and_flags 53 | line = [ 54 | e.name, 55 | ' '.join([normalize_path(x) for x in e.sources]), 56 | ' '.join([x for x in e.extra_objects]), 57 | ' '.join(['-I' + x for x in e.include_dirs]), 58 | ' '.join(['-U' + x for x in e.undef_macros]), 59 | ' '.join(['-L' + x for x in e.library_dirs]), 60 | ' '.join(['-l' + x for x in e.libraries]), 61 | ' '.join([x for x in e.extra_link_args]), 62 | '-D$(__' + e.name + '_DEFS)' if len(defines_and_flags) != 0 else '' 63 | ] 64 | if len(defines_and_flags) != 0: 65 | lines.append(def_line) 66 | line = ' '.join(line) + ' ' 67 | assert '=' not in line 68 | lines.append(line) 69 | 70 | lines = '\n'.join(lines) 71 | lines += '\n' 72 | 73 | with open('modules_to_add', 'w') as f: 74 | f.write(lines) 75 | with open('failed_modules', 'w') as f: 76 | f.write(' ,'.join(self.failed)) 77 | 78 | def setup_fake(*args, **kwargs): 79 | #print((args, kwargs)) 80 | # kwargs['cmdclass']['build_ext'], kwargs['ext_modules'] 81 | kwargs['script_args'] = ['build_ext'] 82 | class MyMyBuildExt(kwargs['cmdclass']['build_ext'], MyBuildExt): 83 | pass 84 | kwargs['cmdclass']['build_ext'] = MyMyBuildExt 85 | from distutils.core import Extension, setup 86 | res.dist = setup(*args, **kwargs) 87 | 88 | def define_to_str(d): 89 | if type(d) == str: 90 | return d 91 | else: 92 | d_val = d[1] 93 | if d_val is not None: 94 | d_val = d_val.replace('"', "\\"*4 + '"') # escape 95 | return d[0] + (('=' + d_val) if d_val is not None else '') 96 | 97 | def normalize_path(s): 98 | return os.path.relpath(s, 'Modules') 99 | 100 | if sys.version_info.major == 2: 101 | plat = setup2.host_platform 102 | if '-' in plat: 103 | plat = plat[:plat.find('-')] 104 | setup2.host_platform = plat 105 | 106 | setup2.setup = setup_fake 107 | setup2.build_ext = MyBuildExt 108 | setup2.sysconfig.get_config_var = sysconfig_get_config_var 109 | setup2.find_executable = lambda x: None 110 | 111 | if hasattr(setup2, 'COMPILED_WITH_PYDEBUG'): 112 | setup2.COMPILED_WITH_PYDEBUG = True 113 | 114 | setup2.main() 115 | 116 | return res.dist 117 | 118 | def main(): 119 | get_setup_build_ext() 120 | 121 | 122 | if __name__ == '__main__': 123 | main() 124 | 125 | -------------------------------------------------------------------------------- /sysconfig_filter.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import os 4 | import glob 5 | import errno 6 | import sysconfig 7 | 8 | def makedirs(dirname): 9 | try: 10 | os.makedirs(dirname) 11 | except OSError as e: 12 | if e.errno != errno.EEXIST: 13 | raise 14 | 15 | def main(): 16 | sysconfig_file = glob.glob("_sysconfigdata*.py") 17 | assert len(sysconfig_file) == 1 18 | sysconfig_file = sysconfig_file[0] 19 | 20 | with open(sysconfig_file, 'r') as f: 21 | sysconfig_locals = {} 22 | exec(f.read(), {}, sysconfig_locals) 23 | build_time_vars = sysconfig_locals['build_time_vars'] 24 | 25 | py_short_ver = '%d.%d' % sys.version_info[:2] 26 | 27 | my_build_time_vars = { 28 | 'TZPATH': '', 29 | 'CC': '/', 30 | 'AR': '/', 31 | 'ARFLAGS': '', 32 | 'CFLAGS': '', 33 | 'LDFLAGS': '', 34 | 'CCSHARED': '', 35 | 'LDSHARED': '/', 36 | 'EXT_SUFFIX': '.cpython-.so', 37 | 'SO': '.cpython-.so', 38 | 'SOABI': 'cpython-', 39 | 'LIBRARY': 'libpython' + py_short_ver + '.a', 40 | 'LDLIBRARY': 'libpython' + py_short_ver + '.so', 41 | } 42 | 43 | for v in set(my_build_time_vars.keys()): 44 | if v not in build_time_vars: 45 | my_build_time_vars.pop(v) 46 | 47 | vars_to_keep = {'HAVE_GETENTROPY', 'HAVE_GETRANDOM', 'HAVE_GETRANDOM_SYSCALL', 'WITH_DOC_STRINGS', 'WITH_FREELISTS', 'WITH_PYMALLOC'} 48 | 49 | for v in vars_to_keep: 50 | if v in build_time_vars: 51 | my_build_time_vars[v] = build_time_vars[v] 52 | 53 | with open(sysconfig_file, 'w') as f: 54 | f.write('# system configuration generated and used by the sysconfig module\n') 55 | f.write('build_time_vars = {' + ', '.join(repr(v) + ': ' + repr(my_build_time_vars[v]) for v in sorted(my_build_time_vars.keys())) + '}\n') 56 | 57 | # 14d98ac31b9f4e5b89284271f03fb77fc81ab624 58 | # 3.2a5 59 | if '-' in os.path.basename(os.path.dirname(sysconfig.get_makefile_filename())): 60 | config_dirname = 'config-' + py_short_ver 61 | else: 62 | config_dirname = 'config' 63 | makedirs(config_dirname) 64 | with open(os.path.join(config_dirname, 'Makefile'), 'w') as f: 65 | pass 66 | 67 | makedirs('../../include/python' + py_short_ver) 68 | with open(os.path.join('../../include/python' + py_short_ver, 'pyconfig.h'), 'w') as f: 69 | pass 70 | with open(os.path.join('../../include/python' + py_short_ver, 'Python.h'), 'w') as f: 71 | pass 72 | 73 | if __name__ == '__main__': 74 | main() 75 | -------------------------------------------------------------------------------- /zipper.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import os 4 | import zipfile 5 | 6 | zipfile.zlib.Z_DEFAULT_COMPRESSION = 9 7 | 8 | def zip_dir(src_dir, dest_zip): 9 | with zipfile.ZipFile(dest_zip, 'w', zipfile.ZIP_DEFLATED) as zip_f: 10 | paths = [] 11 | for root, dirs, files in os.walk(src_dir): 12 | for file in files: 13 | paths.append(os.path.join(root, file)) 14 | 15 | paths = sorted(paths) 16 | 17 | for file in paths: 18 | info = zipfile.ZipInfo(os.path.relpath(file, src_dir)) 19 | info.compress_type = zipfile.ZIP_DEFLATED 20 | info.create_system = 0 21 | with open(file, 'rb') as f: 22 | content = f.read() 23 | zip_f.writestr(info, content) 24 | 25 | def main(): 26 | src_dir = sys.argv[1] 27 | dest_zip = sys.argv[2] 28 | zip_dir(src_dir, dest_zip) 29 | 30 | if __name__ == '__main__': 31 | main() 32 | --------------------------------------------------------------------------------