Compare commits

...
Sign in to create a new pull request.
Author SHA1 Message Date
f8704e8ace add code to fix 2025-02-24 00:13:51 +00:00

208
make_package.hs Normal file
View file

@ -0,0 +1,208 @@
-- credits for the original code: Yair Chuchem
import Control.Exception (bracket_)
import qualified Control.Lens as Lens
import Control.Lens.Operators
import Control.Monad (when, unless, filterM)
import Data.Foldable (traverse_)
import qualified System.Directory as Dir
import qualified System.Environment as Env
import System.FilePath ((</>), takeFileName, takeDirectory)
import qualified System.Info as SysInfo
import System.Process (readProcess, callProcess)
import Prelude
interestingLibs :: [String]
interestingLibs =
[ "libGLEW"
, "libGLU"
, "libXcursor"
, "libXi"
, "libXinerama"
, "libXrandr"
, "libXrender"
, "libbsd"
, "libbz2"
, "libcares"
, "libcrypto"
, "libdw"
, "libelf"
, "libgflags"
, "libgmp"
, "libicudata"
, "libicui18n"
, "libicuuc"
, "liblzma"
, "libnghttp2"
, "libnode"
, "librocksdb"
, "libsnappy"
, "libssl"
, "libuv"
]
isInteresting :: FilePath -> Bool
isInteresting path =
baseName `elem` interestingLibs
where
-- takeBaseName removes one extension, we remove all:
baseName = takeFileName path & takeWhile (/= '.')
parseLddOut :: String -> [FilePath]
parseLddOut lddOut =
lines lddOut
>>= parseLine
& filter isInteresting
where
parseLine line =
case words line & dropWhile (/= "=>") of
[] -> []
"=>":libPath:_ -> [libPath]
_ -> error "unexpected break output"
otoolMinMacosVersion :: FilePath -> IO Float
otoolMinMacosVersion path =
readProcess "otool" ["-l", path] "" <&>
(^?! Lens.to lines . traverse .
Lens.to words .
Lens.filteredBy (Lens.ix 0 . (Lens.only "minos" <> Lens.only "version")) .
Lens.ix 1 . Lens._Show)
-- Use `otool` to recursively find macOS deps
findDylibs :: FilePath -> IO [FilePath]
findDylibs path =
do
deps <-
readProcess "otool" ["-L", path] ""
<&> lines <&> tail <&> map words <&> (>>= take 1)
<&> filter (/= path)
>>= filterM Dir.doesPathExist
traverse findDylibs deps <&> concat <&> (deps <>)
-- Slightly nicer syntax than using a sum type with case everywhere
isMacOS :: Bool
isMacOS = SysInfo.os == "darwin"
isWindows :: Bool
isWindows = SysInfo.os == "mingw32"
isLinux :: Bool
isLinux = SysInfo.os == "linux"
pkgDir :: FilePath
pkgDir
| isMacOS = "Lamdu.app"
| otherwise = "lamdu"
toPackageWith :: FilePath -> FilePath -> IO ()
toPackageWith srcPath relPath =
do
putStrLn $ "Packaging " ++ srcPath ++ " to " ++ destPath
Dir.createDirectoryIfMissing True (takeDirectory destPath)
callProcess "cp" ["-aLR", srcPath, destPath]
where
destPath = contentsDir </> relPath
contentsDir
| isMacOS = pkgDir </> "Contents"
| otherwise = pkgDir
toPackage :: FilePath -> IO ()
toPackage srcPath = toPackageWith srcPath (takeFileName srcPath)
libToPackage :: FilePath -> IO ()
libToPackage srcPath =
toPackageWith srcPath (dir </> filename)
where
filename = takeFileName srcPath
dir
| isWindows = "."
| isMacOS = "MacOS"
| otherwise = "lib"
findDeps :: String -> IO [FilePath]
findDeps exec
| isWindows =
[ "libwinpthread-1.dll"
, "libstdc++-6.dll"
, "libgcc_s_seh-1.dll"
, "librocksdb.dll"
, "libbz2-1.dll"
, "liblz4.dll"
, "zlib1.dll"
, "libzstd.dll"
] <&> ("/mingw64/bin" </>)
& pure
| isMacOS =
findDylibs exec
| otherwise =
readProcess "ldd" [exec] "" <&> parseLddOut
fixDylibPaths :: FilePath -> IO ()
fixDylibPaths targetName =
findDylibs target >>=
traverse_ fixDep
where
target = pkgDir </> "Contents" </> "MacOS" </> targetName
fixDep dep =
do
callProcess "chmod" ["+w", target]
callProcess "install_name_tool"
["-change", dep, "@executable_path/" ++ takeFileName dep, target]
parseLamduVersion :: String -> String
parseLamduVersion info =
case lines info <&> words of
(["Lamdu", result]:_) -> result
_ -> error "failed parsing version number"
whichCmd :: String
whichCmd
| isWindows = "where"
| otherwise = "which"
main :: IO ()
main =
do
[lamduExec] <- Env.getArgs
nodePath <- readProcess whichCmd ["node"] "" <&> takeWhile (`notElem` "\r\n")
nodeDeps <- findDeps nodePath
when isMacOS $
do
unless (null nodeDeps) $ fail "nodejs not statically linked!"
minos <- otoolMinMacosVersion lamduExec
when (minos > 10.9) (fail "Lamdu executable only runs on new macOS versions")
version <- readProcess lamduExec ["--version"] "" <&> parseLamduVersion
lamduDeps <- findDeps lamduExec
let allDeps = nodeDeps <> lamduDeps
bracket_ (Dir.createDirectory pkgDir) (unless isMacOS (Dir.removeDirectoryRecursive pkgDir)) $
do
toPackageWith lamduExec destPath
toPackageWith "data" dataDir
toPackageWith nodePath (dataDir </> "bin/node.exe")
traverse_ libToPackage allDeps
when isWindows $
callProcess "iscc.exe" ["/Flamdu-" ++ version ++ "-win-setup", "tools\\data\\lamdu.iss"]
when isLinux $
do
toPackage "tools/data/run-lamdu.sh"
callProcess "tar" ["-c", "-z", "-f", "lamdu-" ++ version ++ "-linux.tgz", pkgDir]
when isMacOS $
do
toPackage "tools/data/Info.plist"
traverse_ fixDylibPaths ("lamdu" : (allDeps <&> takeFileName))
callProcess "sh"
[ "tools/data/macos_icon.sh"
, "data/Lamdu.png"
, pkgDir </> "Contents" </> "Resources" </> "lamdu.icns"
]
-- The next steps of signing, notarization, and stapling happen in a seperate script
putStrLn "Done"
where
destPath
| isWindows = "lamdu.exe"
| isMacOS = "MacOS/lamdu"
| otherwise = "bin/lamdu"
dataDir
| isMacOS = "Resources"
| otherwise = "data"