{ lib , stdenv # infrastructure , populateHawkSourceInfo , replaceDotWithUnderscore , buildDotnetModule , fetchpatch , fetchzip , hardLinkJoin , launchScriptsFor , makeDesktopItem , releaseTagSourceInfos , runCommand , symlinkJoin , writeShellScriptBin # makedeps , git # rundeps , gnome-themes-extra , gtk2-x11 , libgdiplus , libGL , lua , mono , monoBasic , openal , SDL2 , udev , zstd # other parameters , buildConfig , doCheck , extraDefines , extraDotnetBuildFlags }: let getMainOutput = lib.getOutput "out"; /** to override just one, you'll probably want to manually import packages-managed.nix, and combine that with the output of this */ buildExtraManagedDepsFor = hawkSourceInfo: let pm = import ./packages-managed.nix { inherit lib buildDotnetModule runCommand hawkSourceInfo; }; in symlinkJoin { name = "bizhawk-managed-deps"; paths = builtins.map (s: pm.${s}) (lib.sort lib.lessThan hawkSourceInfo.neededExtraManagedDeps); }; /** * NOTE: This impl. is unfinished, only useful for checking that overriding is possible. * For actually overriding `extraUnmanagedDeps`, roll your own `symlinkJoin` impl. or something. * * TODO replace this with something more like `buildExtraManagedDepsFor`; for existing Nix exprs see https://gitlab.com/YoshiRulz/yoshis-hawk-thoughts/-/issues/10 */ buildUnmanagedDepsFor = hawkSourceInfo: stdenv.mkDerivation { inherit (hawkSourceInfo) src version; pname = "bizhawk-native-deps"; dontBuild = true; installPhase = '' runHook preInstall mkdir -p $out; cp -vt $out Assets/dll/*.so chmod -x $out/* runHook postInstall ''; dontFixup = true; }; genDepsHostTargetFor = { hawkSourceInfo, gtk2-x11' ? getMainOutput gtk2-x11, mono' ? mono }: [ gtk2-x11' (getMainOutput libgdiplus) lua mono' openal (getMainOutput zstd) ] ++ lib.optionals hawkSourceInfo.needsSDL [ SDL2 (getMainOutput udev) ] ++ lib.optional hawkSourceInfo.needsLibGLVND (getMainOutput libGL); /** * see splitReleaseArtifact re: outputs * and no you can't build only DiscoHawk and its deps; deal with it */ buildAssembliesFor = hawkSourceInfo': let hawkSourceInfo = populateHawkSourceInfo hawkSourceInfo'; extraManagedDeps = hawkSourceInfo.extraManagedDeps or buildExtraManagedDepsFor hawkSourceInfo; in buildDotnetModule (lib.fix (finalAttrs: { # proper `finalAttrs` not supported >:( inherit doCheck gnome-themes-extra mono; inherit (hawkSourceInfo) __contentAddressed dotnet-sdk nugetDeps src version; pname = "BizHawk"; isLocalBuild = lib.hasSuffix "-local" finalAttrs.version; postUnpack = lib.optionalString finalAttrs.isLocalBuild ''(cd BizHawk-*-local; Dist/CleanupBuildOutputDirs.sh)''; outputs = [ "out" "assets" "extraUnmanagedDeps" "waterboxCores" ]; propagatedBuildOutputs = []; # without this, other outputs depend on `out` strictDeps = true; nativeBuildInputs = lib.optional finalAttrs.doCheck finalAttrs.mono ++ lib.optional finalAttrs.isLocalBuild git; gtk2-x11 = getMainOutput gtk2-x11; buildInputs = genDepsHostTargetFor { inherit hawkSourceInfo; gtk2-x11' = finalAttrs.gtk2-x11; mono' = finalAttrs.mono; }; patches = lib.optional (!hawkSourceInfo.hasMiscTypeCheckerPatch_6afb3be98) (fetchpatch { url = "https://github.com/TASEmulators/BizHawk/commit/6afb3be98cd3d8bf111c8e61fdc29fc3136aab1e.patch"; hash = "sha512-WpLGbng7TkEHU6wWBfk0ogDkK7Ub9Zh5PKkIQZkXDrERkEtQKrdTOOYGhswFEfJ0W4Je5hl5iZ6Ona140BxhhA=="; }); postPatch = '' sed -e 's/SimpleSubshell("uname", "-r", [^)]\+)/"${builtins.toString stdenv.hostPlatform.uname.release}"/' \ -e 's/SimpleSubshell("uname", "-s", [^)]\+)/"${builtins.toString stdenv.hostPlatform.uname.system}"/' \ -i src/BizHawk.Common/OSTailoredCode.cs sed '/Assets\/\*\*/d' -i ${if hawkSourceInfo.copyingAssetsInEmuHawkProj # stop MSBuild from copying Assets, we'll do that in installPhase then "src/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj" else "src/BizHawk.Common/BizHawk.Common.csproj"} rm -fr References; cp -LrT '${extraManagedDeps}' References ''; buildType = buildConfig; #TODO move debug symbols to `!debug`? extraDotnetBuildFlags = let s = lib.optionalString (extraDefines != "") "-p:MachineExtraCompilationFlag=${extraDefines} "; in "-maxcpucount:$NIX_BUILD_CORES -p:BuildInParallel=true --no-restore -v normal -p:ContinuousIntegrationBuild=true" + " -p:DebugType=portable ${s}${extraDotnetBuildFlags}"; buildPhase = '' runHook preBuild ${if hawkSourceInfo.versionProjNeedsDoubleBuild then ''cd src/BizHawk.Version dotnet build ${finalAttrs.extraDotnetBuildFlags} cd ../.. '' else ""}Dist/Build${finalAttrs.buildType}.sh ${finalAttrs.extraDotnetBuildFlags} runHook postBuild ''; checkNativeInputs = finalAttrs.buildInputs; # doesn't work??? checkPhase = '' runHook preCheck export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${lib.makeLibraryPath finalAttrs.checkNativeInputs}" ${if hawkSourceInfo.testProjectNeedsCIEnvVar then ''export GITLAB_CI=1 '' else ""}Dist/BuildTest${finalAttrs.buildType}.sh ${finalAttrs.extraDotnetBuildFlags} # can't build w/ extra Analyzers, it fails to restore :( Dist/Build${finalAttrs.buildType}.sh -p:MachineRunAnalyzersDuringBuild=true ${finalAttrs.extraDotnetBuildFlags} runHook postCheck ''; installPhase = '' runHook preInstall ${if hawkSourceInfo.packageScriptRemovesWinFilesFromLinux then "" else ''( cd Assets/dll; rm -f "lua54.dll" \ "mupen64plus-audio-bkm.dll" "mupen64plus-input-bkm.dll" "mupen64plus-rsp-cxd4-sse2.dll" "mupen64plus-rsp-hle.dll" "mupen64plus-video-angrylion-rdp.dll" "mupen64plus-video-glide64.dll" "mupen64plus-video-glide64mk2.dll" "mupen64plus-video-GLideN64.dll" "mupen64plus-video-rice.dll" "mupen64plus.dll" "octoshock.dll" \ "bizlynx.dll" "bizswan.dll" "blip_buf.dll" "libbizhash.dll" "libdarm.dll" "libemu83.dll" "libfwunpack.dll" "libgambatte.dll" "libLibretroBridge.dll" "libquicknes.dll" "librcheevos.dll" "libsameboy.dll" "mgba.dll" "MSXHawk.dll" "waterboxhost.dll" ) ''}rm Assets/EmuHawkMono.sh # replaced w/ launch-scripts.nix cp -avT Assets $assets ${if hawkSourceInfo.hasAssetsInOutput then ''sed '/packaged_output\/dll/d' -i Dist/Package.sh rm output/dll/*.xml # that's what that Package.sh line did, though I've forgotten why and don't care to rediscover it mv -t $assets/dll output/dll/* '' else ""}chmod -x $assets/dll/*.so # no idea why these are all executable mkdir -p $extraUnmanagedDeps/lib; mv -t $extraUnmanagedDeps/lib $assets/dll/*.so* mkdir -p $waterboxCores/dll; mv -t $waterboxCores/dll $assets/dll/*.wbx* ${if hawkSourceInfo.packageScriptNeeds7Zip then ''sed '/7za a -t7z -mx9/d' -i Dist/Package.sh # not worth figuring this out '' else ""}Dist/Package.sh linux-x64 ${if hawkSourceInfo.hasAssetsInOutput then ''mkdir packaged_output/dll '' else ""}mkdir -p $out; cp -avt $out packaged_output/*.exe* packaged_output/dll mv -t $out/dll $assets/dll/* mv -t $out $assets/defctrl.json $assets/gamedb $assets/Shaders printf '${hawkSourceInfo.frontendPackageFlavour}' >$out/dll/custombuild.txt runHook postInstall ''; dontPatchELF = true; passthru = { inherit extraManagedDeps # could use this to backport changes to ExternalProjects? IDK hawkSourceInfo; # simple way to override `nugetDeps` for patching: `buildAssembliesFor (bizhawkAssemblies-latest.hawkSourceInfo // { nugetDeps = /*...*/; })` inherit (finalAttrs) gnome-themes-extra mono; # extraUnmanagedDeps = buildUnmanagedDepsFor hawkSourceInfo; # this will override the output of the same name, example: `buildEmuHawkInstallableFor { bizhawkAssemblies = bizhawkAssemblies-latest // { extraUnmanagedDeps = /*...*/; }; }` # can similarly override `assets` output, only used by launch script to populate `BIZHAWK_DATA_HOME` if the dir doesn't exist at runtime, # and `waterboxCores` output, which holds just the Waterbox cores, as the name suggests }; meta.sourceProvenance = [ lib.sourceTypes.binaryNativeCode ]; # `extraUnmanagedDeps` and `waterboxCores` outputs; will work on from-source later })); buildInstallable = { bizhawkAssemblies , pname , launchScript , desktopName , desktopIcon }: let exeName = "${pname}-${bizhawkAssemblies.hawkSourceInfo.version}"; in symlinkJoin { inherit (bizhawkAssemblies.hawkSourceInfo) __contentAddressed; name = exeName; paths = [ (let # in versions < 2.9.2, Waterbox cores load from `Assembly.GetEntryAssembly().Location + "/../dll"`, but `Location` resolves symlinks so only `!bin` is visible -_- farm = (if bizhawkAssemblies.hawkSourceInfo.exePathRespectsEnvVar then symlinkJoin else hardLinkJoin) { inherit (bizhawkAssemblies.hawkSourceInfo) __contentAddressed; name = "bizhawk-asms-and-wbox"; paths = [ bizhawkAssemblies bizhawkAssemblies.waterboxCores ]; }; in writeShellScriptBin exeName ''BIZHAWK_HOME='${farm}' exec '${launchScript}' "$@"'') (makeDesktopItem { inherit desktopName; # actually Name exec = exeName; icon = desktopIcon; name = exeName; # actually filename }) ]; passthru = { inherit (bizhawkAssemblies) fetch-deps hawkSourceInfo; assemblies = bizhawkAssemblies; }; meta = let p = lib.systems.inspect.patterns; in { platforms = [ (p.isLinux // p.isAarch64) (p.isLinux // p.isx86_32) (p.isLinux // p.isx86_64) # `isMacOS` seems to be unused in Nixpkgs, though most mentions of `isDarwin` aren't from `lib.systems.inspect.patterns` so maybe it's only right for the legacy method # won't bother w/ PPC or x86 Macs, they're too weak (p.isDarwin // p.isAarch64) (p.isDarwin // p.isx86_64) # not sure where Nix' Windows support is at right now, whether isWindows is the relevant pattern, or whether Nix on Windows will be suitable for us once it is usable # (p.isWindows // p.isAarch64) # (p.isWindows // p.isx86_32) # (p.isWindows // p.isx86_64) ]; badPlatforms = [ p.isDarwin p.isAarch64 p.isx86_32 ]; mainProgram = exeName; }; }; buildInstallable' = { hawkSourceInfo , bizhawkAssemblies , forNixOS , pname , launchScriptAttrName , desktopName , desktopIcon }: let bizhawkAssembliesFinal = if bizhawkAssemblies != null then lib.traceIf (hawkSourceInfo != null) "`hawkSourceInfo` passed to `build{EmuHawk,DiscoHawk}InstallableFor` will be ignored in favour of `bizhawkAssemblies.hawkSourceInfo`" bizhawkAssemblies else assert lib.assertMsg (hawkSourceInfo != null) "must pass either `hawkSourceInfo` or `bizhawkAssemblies` to `build{EmuHawk,DiscoHawk}InstallableFor`"; buildAssembliesFor (lib.optionalAttrs forNixOS { needsLibGLVND = true; } // hawkSourceInfo); in buildInstallable { inherit desktopName pname; bizhawkAssemblies = bizhawkAssembliesFinal; desktopIcon = if desktopIcon != null then desktopIcon else pname; launchScript = (launchScriptsFor bizhawkAssembliesFinal false).${launchScriptAttrName}; }; in { inherit buildAssembliesFor buildExtraManagedDepsFor buildUnmanagedDepsFor; /** * assemblies and (managed) dependencies, and some other immutable things like the gamedb, are in the `out` output; * unmanaged/native dependencies are in the `extraUnmanagedDeps` output (under `/lib`); * Waterbox cores are in the `waterboxCores` output (under `/dll`); * the rest of the "assets" (bundled scripts, palettes, etc.) are in the `assets` output */ splitReleaseArtifact = { hawkSourceInfo , hashPrePatching , crossPlatformArtifact ? false , zippedTarball ? false , url ? "https://github.com/TASEmulators/BizHawk/releases/download/${hawkSourceInfo.version}/BizHawk-${hawkSourceInfo.version}${if crossPlatformArtifact then ".zip" else if zippedTarball then "-linux-x64.tar.zip" else "-linux-x64.tar.gz"}" , stripRoot ? true }: assert buildConfig == "Release"; let artifact = fetchzip { inherit stripRoot url; hash = hashPrePatching; }; hawkSourceInfo' = populateHawkSourceInfo hawkSourceInfo; in runCommand "BizHawk-${hawkSourceInfo.version}-bin" { buildInputs = genDepsHostTargetFor { hawkSourceInfo = hawkSourceInfo'; }; # is using `buildInputs` like this correct? it's necessary because the launch script reads from it outputs = [ "out" "assets" "extraUnmanagedDeps" "waterboxCores" ]; passthru = { inherit gnome-themes-extra mono; hawkSourceInfo = hawkSourceInfo'; }; meta.sourceProvenance = [ lib.sourceTypes.binaryNativeCode lib.sourceTypes.binaryBytecode ]; } '' ${if zippedTarball then ''mkdir -p $assets; tar -xf '${artifact}'/*.tar -C $assets'' else ''cp -aT '${artifact}' $assets''} cd $assets find . -type d -exec chmod +w {} \; rm -f EmuHawkMono.sh ${if hawkSourceInfo'.releaseArtifactHasRogueOTKAsmConfig then ''mv -ft dll OpenTK.dll.config '' else ""}rmdir Firmware mkdir -p ExternalTools; touch ExternalTools/.keep mkdir -p $out; mv -t $out defctrl.json DiscoHawk.exe* dll EmuHawk.exe* gamedb [Ss]haders ${if hawkSourceInfo'.releaseArtifactNeedsVBDotnetReference then ''cp -t $out/dll '${getMainOutput monoBasic}/usr/lib/mono/4.5/Microsoft.VisualBasic.dll' '' else ""}${if hawkSourceInfo'.releaseArtifactNeedsLowercaseAsms then ''(cd $out/dll; for s in Client.Common Emulation.Cores; do cp BizHawk.$s.dll Bizhawk.$s.dll; done) '' else ""}${if hawkSourceInfo'.releaseArtifactNeedsOTKAsmConfig then ''cp -t $out/dll '${releaseTagSourceInfos.info-2_6.src}/Assets/dll/OpenTK.dll.config' '' else ""}printf '${hawkSourceInfo'.frontendPackageFlavour}' >$out/dll/custombuild.txt mkdir -p $extraUnmanagedDeps/lib; mv -t $extraUnmanagedDeps/lib $out/dll/*.so* mkdir -p $waterboxCores/dll; mv -t $waterboxCores/dll $out/dll/*.wbx* ''; buildDiscoHawkInstallableFor = { bizhawkAssemblies ? null , hawkSourceInfo ? null , forNixOS ? true # currently only adds Mesa to buildInputs, and DiscoHawk doesn't need that, but it's propagated through here so the asms derivation can be shared between it and EmuHawk , desktopName ? "DiscoHawk (Mono Runtime)" , desktopIcon ? null }: buildInstallable' { inherit bizhawkAssemblies desktopIcon desktopName forNixOS hawkSourceInfo; pname = "discohawk-monort"; launchScriptAttrName = "discohawk"; }; buildEmuHawkInstallableFor = { bizhawkAssemblies ? null , hawkSourceInfo ? null , forNixOS ? true , desktopName ? "EmuHawk (Mono Runtime)" , desktopIcon ? null }: buildInstallable' { inherit bizhawkAssemblies desktopIcon desktopName forNixOS hawkSourceInfo; pname = "emuhawk-monort"; launchScriptAttrName = if forNixOS then "emuhawk" else "emuhawkNonNixOS"; }; }