#!/usr/bin/env python3 import os import shutil import sys import platform import subprocess import tempfile prefix = '/usr/local' clean = True defaultimpl = 'kernel' darwin = platform.system() == 'Darwin' def hostsanitize(host): host = host.split('-')[0] host = ''.join(c for c in host if c in '_0123456789abcdefghijklmnopqrstuvwxyz') for prefix,result in ( ('amd64','amd64'), ('x86_64','amd64'), ('x86','x86'), ('i386','x86'), ('i686','x86'), ('arm64','arm64'), ('armv8','arm64'), ('aarch64','arm64'), ('arm','arm32'), ('riscv64','riscv64'), ('riscv','riscv32'), ('mips64','mips64'), ('mips','mips32'), ('ppc64','ppc64'), ('powerpc64','ppc64'), ('powerpc','ppc32'), ('ppc','ppc32'), ('sparc64','sparc64'), ('sparcv9','sparc64'), ('sun4u','sparc64'), ('sparc','sparc32'), ('sun','sparc32'), ): if host.startswith(prefix): return result return host host = hostsanitize(platform.machine()) configurelog = '' def log(x): global configurelog configurelog += x+'\n' print(x) makefile = '' for arg in sys.argv[1:]: if arg.startswith('--prefix='): prefix = arg[9:] continue if arg.startswith('--host='): host = hostsanitize(arg[7:]) continue if arg == '--clean': clean = True continue if arg == '--noclean': clean = False continue if arg == '--darwin': darwin = True continue if arg == '--nodarwin': darwin = False continue raise ValueError('unrecognized argument %s' % arg) echoargs = './configure' echoargs += ' --prefix=%s' % prefix echoargs += ' --host=%s' % host if clean: echoargs += ' --clean' if not clean: echoargs += ' --noclean' if darwin: echoargs += ' --darwin' if not darwin: echoargs += ' --nodarwin' log(echoargs) if prefix[0] != '/': raise ValueError('prefix %s is not an absolute path' % prefix) if clean: shutil.rmtree('build/%s' % host,ignore_errors=True) def dirlinksym(dir,source,target): with tempfile.TemporaryDirectory(dir=dir) as t: os.symlink(target,'%s/symlink' % t) os.rename('%s/symlink' % t,'%s/%s' % (dir,source)) os.makedirs('build/%s' % host,exist_ok=True) os.makedirs('build/%s/package/bin' % host,exist_ok=True) os.makedirs('build/%s/package/lib' % host,exist_ok=True) os.makedirs('build/%s/package/include' % host,exist_ok=True) if clean: os.symlink('../..','build/%s/src' % host) def copytree(src,dst,acceptfn=None): # starting with python 3.8 can use shutil.copytree # with dirs_exist_ok=True # but want to support older versions of python too # and want to symlink rather than copying os.makedirs(dst,exist_ok=True) for fn in sorted(os.listdir(src)): srcfn = '%s/%s' % (src,fn) if os.path.isdir(srcfn): dstfn = '%s/%s' % (dst,fn) copytree(srcfn,dstfn) else: if acceptfn is not None: if not acceptfn(fn): continue dirlinksym(dst,fn,'../'*(len(dst.split('/'))-2)+'src/'+srcfn) shutil.copystat(src,dst) copytree('scripts-build','build/%s/scripts' % host) with open('project/library') as f: projectlibrary = f.read().strip() # ----- shared-library variations so = 'dylib' if darwin else 'so' so1 = '1.dylib' if darwin else 'so.1' soname = 'install_name' if darwin else 'soname' with open(f'build/{host}/scripts/sharedlib-so1','w') as f: f.write(so1+'\n') with open(f'build/{host}/scripts/sharedlib-soname','w') as f: f.write(soname+'\n') # ----- compilers def compilerversion(c): try: p = subprocess.Popen(c.split()+['--version'],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,universal_newlines=True) out,err = p.communicate() assert not err assert not p.returncode return out except: pass firstcompiler = None with open('compilers/default') as f: for c in f.readlines(): c = c.strip() cv = compilerversion(c) if cv == None: log('skipping default compiler %s' % c) continue log('using default compiler %s' % c) firstcompiler = c break if firstcompiler is None: raise ValueError('did not find a working compiler') with open('build/%s/scripts/cdcompile' % host,'w') as f: f.write('#!/bin/sh\n') f.write('\n') f.write('cd "$1"; shift\n') f.write('exec %s "$@"\n' % firstcompiler) os.chmod('build/%s/scripts/cdcompile' % host,0o755) # ----- projectlibrary def alternatives(dir,fn): with open('%s/%s' % (dir,fn)) as f: for line in f: line = line.split() if len(line) >= 3 and line[:2] == ['//','automatic-alternatives']: return ' '.join(line[2:]) return '1' objects = {} for dir in 'kernel','openssl','include': builddir = 'build/%s/%s' % (host,dir) os.makedirs(builddir,exist_ok=True) for fn in sorted(os.listdir(dir)): dirlinksym(builddir,fn,'../src/%s/%s' % (dir,fn)) if dir == 'include': shutil.copy2('include/%s'%fn,'build/%s/package/include/%s'%(host,fn)) if fn.endswith('.c'): impl = alternatives(dir,fn) base = fn[:-2] M = '%s/%s.o: %s/%s.c' % (dir,base,dir,base) for obj in impl.split()[1:]: if obj.endswith('.o') and not obj.startswith('-l'): M += ' %s/%s' % (dir,obj) M += '\n' M += '\tscripts/compilealternatives %s %s %s\n' % (dir,base,alternatives(dir,fn)) M += '\n' makefile = M + makefile if dir not in objects: objects[dir] = [] objects[dir] += ['%s/%s.o'%(dir,base)] for impl in sorted(objects): M = 'package/lib/lib%s-%s.a: scripts/staticlib %s\n' % (projectlibrary,impl,' '.join(objects[impl])) M += '\tscripts/staticlib lib%s-%s %s\n' % (projectlibrary,impl,' '.join(objects[impl])) M += '\n' makefile = M + makefile M = f'package/lib/lib%s-%s.{so1}: scripts/sharedlib %s\n' % (projectlibrary,impl,' '.join(objects[impl])) if impl == 'openssl': M += '\tscripts/sharedlib lib%s-%s %s -lcrypto\n' % (projectlibrary,impl,' '.join(objects[impl])) else: M += '\tscripts/sharedlib lib%s-%s %s\n' % (projectlibrary,impl,' '.join(objects[impl])) M += '\n' makefile = M + makefile M = f'package/lib/lib%s-%s.{so}: package/lib/lib%s-%s.{so1}\n' % (projectlibrary,impl,projectlibrary,impl) M += f'\trm -f package/lib/lib%s-%s.{so}\n' % (projectlibrary,impl) M += f'\tln -s lib%s-%s.{so1} package/lib/lib%s-%s.{so}\n' % (projectlibrary,impl,projectlibrary,impl) M += '\n' makefile = M + makefile for link in 'a',so1,so: M = 'package/lib/lib%s.%s: package/lib/lib%s-%s.%s\n' % (projectlibrary,link,projectlibrary,defaultimpl,link) M += '\trm -f package/lib/lib%s.%s\n' % (projectlibrary,link) M += '\tln -s lib%s-%s.%s package/lib/lib%s.%s\n' % (projectlibrary,defaultimpl,link,projectlibrary,link) M += '\n' makefile = M + makefile # ----- command copytree('command','build/%s/command'%host) dirlinksym('build/%s/command'%host,'bin','../package/bin') dirlinksym('build/%s/command'%host,'lib','../package/lib') dirlinksym('build/%s/command'%host,'include','../package/include') with open('build/%s/command/link' % host,'w') as f: f.write('#!/bin/sh\n') f.write('target="$1"; shift\n') f.write('%s \\\n' % firstcompiler) f.write(' -fvisibility=hidden \\\n') f.write(' -o "$target" "$@"\n') os.chmod('build/%s/command/link' % host,0o755) commands = [] for fn in sorted(os.listdir('command')): if not fn.endswith('.c'): continue base = fn[:-2] deps = 'command/%s.o'%base link = 'cd command && ./link bin/%s %s.o'%(base,base) syslink = '' with open('command/%s' % fn) as f: for line in f: line = line.strip().split() if len(line) < 1: continue if line[0] != '#include': continue if '-lm' in line: syslink += ' -lm' if '-l%s'%projectlibrary in line: deps += f' package/lib/lib{projectlibrary}.{so}' link += f' lib/lib{projectlibrary}.{so}' link += syslink M = 'command/%s.o: command/%s.c\n' % (base,base) M += '\tscripts/compiledefault command %s c -I include\n' % base M += '\n' makefile = M + makefile M = 'package/bin/%s: %s\n' % (base,deps) M += '\t%s\n' % link M += '\n' makefile = M + makefile commands += ['package/bin/%s' % base] M = 'commands: %s\n' % ' '.join(commands) M += '\n' makefile = M + makefile # ----- manual pages for manpage in sorted(os.listdir('doc/man')): section = 'man%s' % manpage[-1] targetdir = 'build/%s/package/man/%s' % (host,section) os.makedirs(targetdir,exist_ok=True) shutil.copy2('doc/man/%s'%manpage,'%s/%s'%(targetdir,manpage)) # ----- make install M = 'install: scripts/install default\n' M += '\tscripts/install %s\n' % prefix M += '\n' makefile = M + makefile # ----- make default M = 'default: \\\n' for impl in sorted(objects): M += 'package/lib/lib%s-%s.a \\\n' % (projectlibrary,impl) M += f'package/lib/lib%s-%s.{so} \\\n' % (projectlibrary,impl) M += f'package/lib/lib%s-%s.{so1} \\\n' % (projectlibrary,impl) M += 'package/lib/lib%s.a \\\n' % projectlibrary M += f'package/lib/lib%s.{so} \\\n' % projectlibrary M += f'package/lib/lib%s.{so1} \\\n' % projectlibrary M += 'commands\n' M += '\n' makefile = M + makefile with open('build/%s/Makefile' % host,'w') as f: f.write(makefile) # ----- build/0, build/Makefile dirlinksym('build','0',host) with open('build/Makefile','w') as f: f.write('default:\n') f.write('\tcd %s && $(MAKE)\n' % host) f.write('\n') f.write('install:\n') f.write('\tcd %s && $(MAKE) install\n' % host) f.write('\n') f.write('clean:\n') f.write('\trm -r %s\n' % host) # ----- finish log('configure done') with open('build/%s/configure.log'%host,'a') as f: f.write(configurelog) with open('build/%s/configure.log.latest'%host,'w') as f: f.write(configurelog)