From 347148afe7e3831531d85a8d8921f88872d8a1ea Mon Sep 17 00:00:00 2001 From: Patrick Wuttke Date: Tue, 31 Oct 2023 19:39:51 +0100 Subject: [PATCH] initial commit --- .gitmodules | 3 + SConstruct | 8 ++ external/scons-plus-plus | 1 + private/app/SConscript | 18 +++ private/app/main.cpp | 5 + .../__pycache__/unity_build.cpython-311.pyc | Bin 0 -> 6097 bytes site_scons/site_tools/unity_build.py | 133 ++++++++++++++++++ 7 files changed, 168 insertions(+) create mode 100644 .gitmodules create mode 100644 SConstruct create mode 160000 external/scons-plus-plus create mode 100644 private/app/SConscript create mode 100644 private/app/main.cpp create mode 100644 site_scons/site_tools/__pycache__/unity_build.cpython-311.pyc create mode 100644 site_scons/site_tools/unity_build.py diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..251d615 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "scons-plus-plus"] + path = external/scons-plus-plus + url = https://git.mewin.de/mewin/scons-plus-plus.git diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..aa53139 --- /dev/null +++ b/SConstruct @@ -0,0 +1,8 @@ +config = { + 'PROJECT_NAME': 'spp_template' +} +env = SConscript('external/scons-plus-plus/SConscript', exports = ['config']) +env.Append(CPPPATH = [Dir('private'), Dir('public')]) + +# app +env = SConscript('private/app/SConscript', exports = 'env', variant_dir = env['VARIANT_DIR'], src_dir = '.') diff --git a/external/scons-plus-plus b/external/scons-plus-plus new file mode 160000 index 0000000..93dd09e --- /dev/null +++ b/external/scons-plus-plus @@ -0,0 +1 @@ +Subproject commit 93dd09e3247048996c29b3f8be6f7675cb5e89d5 diff --git a/private/app/SConscript b/private/app/SConscript new file mode 100644 index 0000000..0547c2b --- /dev/null +++ b/private/app/SConscript @@ -0,0 +1,18 @@ + +Import('env') + +# Mijin +lib_mijin = env.Cook('mijin') + +src_files = Split(""" + main.cpp +""") + +prog_app = env.UnityProgram( + target = env['BIN_DIR'] + '/app', + source = src_files, + dependencies = [lib_mijin] +) +env.Default(prog_app) + +Return('env') diff --git a/private/app/main.cpp b/private/app/main.cpp new file mode 100644 index 0000000..e9ad257 --- /dev/null +++ b/private/app/main.cpp @@ -0,0 +1,5 @@ + +int main(int, char**) +{ + return 0; +} diff --git a/site_scons/site_tools/__pycache__/unity_build.cpython-311.pyc b/site_scons/site_tools/__pycache__/unity_build.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d5f6092f2377b6cd1c375ecf6785fabd4ed67b5 GIT binary patch literal 6097 zcmbVQZ)_7s7N7O5?X|s*^T(8cA$CG2IFN)MgqEiK3Hby0gQ;++N=oG#?@>E6sb zw$}lKIv&5BnVos_-pst;dplovy>0|)Vz^Hj@+0&;u52V*sqku^^E!Wl$YJn4x4!fTyD`At$pLgBCLHCa99E_<_qb{2w}i zfZUjeEi(Qk3R5C(l5mI6UgTw2J;QunNwofZ;M=-;=mTbiAX z$wGQI6_DJcsvDUr=cW>L;&Q&RU_CMmxcyEKOA#1y!op9mU>=( zW$rcnY`>5nvmt8@Kl&H)vpkcS81BY8$yy?zfnR+Zmg88TvzQ@Efy%) zC1Ny{5oU>ZC{&VKbz1wAUGUWQ#dV%tbv<`q_p0Z4`+|Jj6X)Zs99F&7`ZFZ0TlLlU zr@#aE%gV}zj@6A~B?D=#3 zgHeOc%%x+~*-KJo*qPx-Y-o6FFggTnUUSI6aJ28_*kIUexcYehj38gm@`f*#j?W0O zDIp_>aXBj*&J7)%8>)D%nt7wuN*T_R7!w3h#K+h0Z*US~M3LAepqSD8q}EtkngVp2 zofR^?;f@OOfG`=KOUZ`olpvp-#h@3q5hOGGCNGJsmkC0GC}9K$g+v#gmn|tEh5Yff!y_6q zsxzZ1Gg>wJiSLUJv?KV(-rx7GH*Ec6=Xb$_-}Ri)8X|f_q)4M3dwxQ;9gd$dQJH4w zYiB?0$WwpSIu*(b11-9BZ@U&dUN`$7%xGv+PP)bS-K7lk$MVfig*E)|C4HsN; z6_zP37;UeNuIcOk1=6@TOR(TkXo-cH9F>_CXxY1=5v&a@*Hvm(DI}*tPvbox3C1;T zlwpO+IIhsVbCtD@q%2-xx~%t;(N(co*KL851C^PS8uT7OZ$}oq3jGJfyA%$nTk!(* z+;J?_DUN)VHdQ?8tTO;!!>D=K@dY0)@iY$9NWC6>K$Xv{=Ak$hpHlz6t+JM@oBaIE zvz8OXTIc1t8FIeGklb3~%zK|T26U^tLtFV!sC@Dr3X5g0oc}3oNWO@Or3~8)ha4BD z1le$yNcSVQ3$YoK7cjx5I1YHulHet2 zEHCVp!l6kqn+_3&6Efqsa09^pBm{f1kcpX78FV^+)nL+b`Le+zgk;K~Qvw8?>14)m zC#7UYlH-|#V9=5bE+T$On#DbBS;=6r|KO9NWH6+Sy%d*(Ogt?Zb}^or62RvXh!=6_ zVc2KIWJV4*5?mMeVXZoFyDJe-Toz)$$6#O$B*QUtg*+QA(2u=|WN;-%VAecY`LZC! zu(p!n2Q^A6NwHaICjO`198lHb!!EPpp(LZ;T>2Q6K1Rf+ll)bquF}WU>5?!7O9H_- zZ`6^#WzQ*L1w*xI(ezKQa<}=m8jsucS}L1}r=(+DfTJWMCyr{A})&nhS@ZefhJu;?) zw+ck`KtyGmf7sq~JN#My3a7RA=CX?TTE*-nk^)Y(obtOs`9e))c2-@U-T zl_4!~P!Al$lQuOk2eqcK-UKXIR~;d_{z53M?mUh2{@2XH*HWor=jmLez_r}xcHiT6 z=QA4DqjNnf*HiGf=cAf`Z*H*A*p}~Kq5d?eHFf|i&iC=zkIojh?I~>AoflU6^GSW% zfubFGcl?BCZ{5$B6wBm)Zp8U=*ECN+_XL*X_dG$>6I|yS)W+dE(R@JL*{SdBgo>Fp zZbausRBohD-?CJvUVLZ8p&l9ic1AmLL9L&`nV&8dd?}Sp5hwg56%jFvL+BHMhj=$Z5cg}5y$<0tbF+p~Cl>{5xx&%?pHg&i{m zf182;#QzNr1%pv3fg{2qm6Fw+h)1j4p;7~e#GNw>c7@89-S!3_{8X4)@KkJ1=~?r^ zLZO@EH8PgnAvk-GxAMs-24McG5TYUhIMMQ_Rjnj9Z`AOwa$hz4UwAtIOOBR7DeLgAaj@%55vP?Qt&M<;-i4S%K_AR5qmE27$&b^@-mRH!^GPG+%O0P zPz+*oE}a$Od!K~XlHn#bf~XQ+2ouf-hLH&fNx}>10T4i^rY(zOg+~A4Xu-Foh}`ye zLUQ!FXUkIJ&LPbc(mkOfa=F^q_Z(i6v^~B0o?eY>&y6j26u9R5T<{(j%ySyosdJqw z*ID4U-sf8GaV^X98rPw79V*G|-mS~@opH_ErhD6p$nI*d45sgTG;Y7n?N>=QyGAu{ ztL|+D+qHCsnRTvRCHX;lDoNvZ>D(@r^PW@Y$6GQp z6*8j$hw1a@>q6$fhKQ%^0!%ps1I*Jv6US>F39OZb)q?Fd_qq-D!vx1G(n%nTXdOJ! zyC3%6>|JiX=YbU}<$`DAPSf%m`M%p1e%ErlMfHq8{)@DB_{(dn*UTEG%*f`|#CH^A zHR}oFShRlTYQp+eMRwC=Xabn3G9`$1Ywlut&;p}S0BDB+g6x%XPlgzt;q6w#UT`Q( z9^2NY1h*m%*cs>M=T_m^y` zL0x%FU7WXFyg2XPmCPh?kQZv3x3#&$c5_p)of}LRzNQVQD8N-7L~HgdB3$N$oi$r6 zj^e)5q~WOwqzwBcj^D^do@w=^qtFVSMNIIKm%az`)T8iVOJKS0lMDKm7Z)SzE^jWg zoX}itx(h6pws+>^g#Z|&&DBXrZnWSJ>i#|V{oVKc-D}L+q~<@W`;UT;=DZ)D`skGE z-)tZaUUWzG>1F!m}07Pq4eJyeLsE(!HB7#RPX=|kb`O9R8 l@=?2&$8oRSwWQ5PjZ$mC20x!bStr%Ga;=Eq@l-+<<^L39M%w@Y literal 0 HcmV?d00001 diff --git a/site_scons/site_tools/unity_build.py b/site_scons/site_tools/unity_build.py new file mode 100644 index 0000000..b822ff7 --- /dev/null +++ b/site_scons/site_tools/unity_build.py @@ -0,0 +1,133 @@ + +import os +import math +from SCons.Script import * +from SCons.Node.FS import File +from SCons import Action + +""" +Scons Unity Build Generator + +Provides several generators for SCons to combine multiple source files into a bigger +one to reduce compilation time, so called "unity builds". This is achieved by generating +unity source files which in term include the actual source files and compile them using +one of the existing SCons builders. + +Usage +----- +In order to use this, just place it inside your `site_scons/site_tools` folder, enable it by +adding "unity_build" to the tools when constructing your Environment and replace invocations +of the Program/Library/SharedLibrary/StaticLibrary builders with their Unity... counterpart: + +env = Environment(tools = ['default', 'unity_build']) + +source_files = ... + +env.UnityProgram( + target = 'my_program', + source = source_files, + ... +) + +The tool will generate an amount of unity source files and invoke the Program builder on these, +forwarding any other arguments you passed. + +Other Options +------------ +You can control the behaviour of the builder using several Environment options: +env['UNITY_CACHE_DIR'] = '.unity' # Directory where the unity sources are stored. + # can be either a string or a Dir() node. +env['UNITY_MAX_SOURCES'] = 15 # Maximum number of source files per unity file. +env['UNITY_MIN_FILES'] = env.GetOption('num_jobs') + # Minimum number of unity files to generate (if possible). + # Defaults to the number of jobs passed to SCons. +env['UNITY_DISABLE'] = False # Set to True to completely disable unity builds. The commands + # will simply pass through their options to the regular builders. + +Additionally any generator can be passed a `cache_dir` to overwrite the value from the Environment. +""" + +def exists(env : Environment): + return True + +def generate(env : Environment): + env.AddMethod(_make_generator(env.Program), 'UnityProgram') + env.AddMethod(_make_generator(env.Library), 'UnityLibrary') + env.AddMethod(_make_generator(env.StaticLibrary), 'UnityStaticLibrary') + env.AddMethod(_make_generator(env.SharedLibrary), 'UnitySharedLibrary') + + # build for generating the unity source files + unity_source_builder = env.Builder( + action = Action.Action(_generate_unity_file, _generate_unity_file_msg) + ) + env.Append(BUILDERS = {'UnitySource': unity_source_builder}) + + env.SetDefault(UNITY_CACHE_DIR = '.unity') + env.SetDefault(UNITY_MAX_SOURCES = 15) + env.SetDefault(UNITY_MIN_FILES = env.GetOption('num_jobs')) + env.SetDefault(UNITY_DISABLE = False) + +def _make_generator(base_generator): + def generator(env, source, target, cache_dir = None, *args, **kwargs): + if env['UNITY_DISABLE']: + return base_generator(target = target, source = source, *args, **kwargs) + unity_source_files = [] + source_files, other_nodes = _flatten_source(source) + + max_sources_per_file = max(1, math.ceil(len(source_files) / env['UNITY_MIN_FILES'])) + sources_per_file = min(max_sources_per_file, env['UNITY_MAX_SOURCES']) + + num_unity_files = math.ceil(len(source_files) / sources_per_file) + + if not cache_dir: + cache_dir = env['UNITY_CACHE_DIR'] + if not isinstance(cache_dir, str): + cache_dir = cache_dir.abspath + + os.makedirs(cache_dir, exist_ok=True) + target_base_name = os.path.basename(target) + + for idx in range(num_unity_files): + unity_filename = f'{cache_dir}/{target_base_name}_{idx}.cpp' + unity_source_files.append(unity_filename) + begin = sources_per_file*idx + end = sources_per_file*(idx+1) + env.UnitySource( + target = unity_filename, + source = source_files[begin:end] + ) + + if len(other_nodes) > 0: + print(f'Exluded {len(other_nodes)} node(s) from Unity build.') + return [base_generator(target = target, source = unity_source_files + other_nodes, *args, **kwargs)] + return generator + +def _flatten_source(source : list): + source_files = [] + other_nodes = [] + for ele in source: + if isinstance(ele, list): + more_sources, more_other = _flatten_source(ele) + source_files.extend(more_sources) + other_nodes.extend(more_other) + elif isinstance(ele, File): + source_files.append(ele.abspath) + elif isinstance(ele, str): + source_files.append(ele) + else: + other_nodes.append(ele) + + return source_files, other_nodes + +def _generate_unity_file_msg(target, source, env : Environment): + assert(len(target) == 1) + return f'Generating {str(target[0])} from {len(source)} source files.' + +def _generate_unity_file(target, source, env : Environment): + assert(len(target) == 1) + + unity_filename = target[0].abspath + with open(unity_filename, 'w') as f: + for source_file in source: + fpath = source_file.abspath.replace("\\", "\\\\") + f.write(f'#include "{fpath}"\n')