Skip to content

Commit b70c93a

Browse files
committed
modifications for reproducible builds
* pass SOURCE_DATE_EPOCH etc. through to build env * clean tar entries & gzip mtime * utime() files before zipping * sort file lists before tar/zip * make private_version deterministic Makefile example: export LC_ALL := C export TZ := UTC export SOURCE_DATE_EPOCH := $(shell git log -1 --pretty=%ct) export PYTHONHASHSEED := $(SOURCE_DATE_EPOCH) export BUILD_DATE := $(shell date +'%b %e %Y' -d @$(SOURCE_DATE_EPOCH)) export BUILD_TIME := $(shell date +'%H:%M:%S' -d @$(SOURCE_DATE_EPOCH))
1 parent f2fff78 commit b70c93a

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

pythonforandroid/archs.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ def get_env(self, with_flags_in_cc=True):
234234

235235
env['PATH'] = environ['PATH']
236236

237+
# for reproducible builds
238+
if 'SOURCE_DATE_EPOCH' in environ:
239+
for k in 'LC_ALL TZ SOURCE_DATE_EPOCH PYTHONHASHSEED BUILD_DATE BUILD_TIME'.split():
240+
if k in environ:
241+
env[k] = environ[k]
242+
237243
return env
238244

239245

pythonforandroid/bootstraps/common/build/build.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#!/usr/bin/env python3
22

3+
from gzip import GzipFile
4+
import hashlib
35
import json
46
from os.path import (
57
dirname, join, isfile, realpath,
68
relpath, split, exists, basename
79
)
8-
from os import listdir, makedirs, remove
10+
from os import environ, listdir, makedirs, remove
911
import os
1012
import shlex
1113
import shutil
@@ -161,16 +163,25 @@ def select(fn):
161163
return False
162164
return not is_blacklist(fn)
163165

166+
# cleaning function (for reproducible builds)
167+
def clean(tinfo):
168+
tinfo.uid = tinfo.gid = 0
169+
tinfo.uname = tinfo.gname = ''
170+
tinfo.mtime = 0
171+
return tinfo
172+
164173
# get the files and relpath file of all the directory we asked for
165174
files = []
166175
for sd in source_dirs:
167176
sd = realpath(sd)
168177
compile_dir(sd, optimize_python=optimize_python)
169178
files += [(x, relpath(realpath(x), sd)) for x in listfiles(sd)
170179
if select(x)]
180+
files.sort() # deterministic
171181

172182
# create tar.gz of thoses files
173-
tf = tarfile.open(tfn, 'w:gz', format=tarfile.USTAR_FORMAT)
183+
gf = GzipFile(tfn, 'wb', mtime=0) # deterministic
184+
tf = tarfile.open(None, 'w', gf, format=tarfile.USTAR_FORMAT)
174185
dirs = []
175186
for fn, afn in files:
176187
dn = dirname(afn)
@@ -189,8 +200,9 @@ def select(fn):
189200
tf.addfile(tinfo)
190201

191202
# put the file
192-
tf.add(fn, afn)
203+
tf.add(fn, afn, filter=clean)
193204
tf.close()
205+
gf.close()
194206

195207

196208
def compile_dir(dfn, optimize_python=True):
@@ -521,9 +533,18 @@ def make_package(args):
521533
versioned_name=versioned_name)
522534

523535
# String resources:
536+
timestamp = time.time()
537+
if 'SOURCE_DATE_EPOCH' in environ:
538+
# for reproducible builds
539+
timestamp = int(environ['SOURCE_DATE_EPOCH'])
540+
private_version = "{} {} {}".format(
541+
args.version,
542+
args.numeric_version,
543+
timestamp
544+
)
524545
render_args = {
525546
"args": args,
526-
"private_version": str(time.time())
547+
"private_version": hashlib.sha1(private_version.encode()).hexdigest()
527548
}
528549
if get_bootstrap_name() == "sdl2":
529550
render_args["url_scheme"] = url_scheme

pythonforandroid/recipes/python3/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import subprocess
44

55
from multiprocessing import cpu_count
6-
from os import environ
6+
from os import environ, utime
77
from os.path import dirname, exists, join
88
from pathlib import Path
99
from shutil import copy2
@@ -385,6 +385,12 @@ def create_python_bundle(self, dirn, arch):
385385
with current_directory(join(self.get_build_dir(arch.arch), 'Lib')):
386386
stdlib_filens = list(walk_valid_filens(
387387
'.', self.stdlib_dir_blacklist, self.stdlib_filen_blacklist))
388+
if 'SOURCE_DATE_EPOCH' in environ:
389+
# for reproducible builds
390+
stdlib_filens.sort()
391+
timestamp = int(environ['SOURCE_DATE_EPOCH'])
392+
for filen in stdlib_filens:
393+
utime(filen, (timestamp, timestamp))
388394
info("Zip {} files into the bundle".format(len(stdlib_filens)))
389395
shprint(sh.zip, stdlib_zip, *stdlib_filens)
390396

0 commit comments

Comments
 (0)