diff options
Diffstat (limited to 'tools/niminst')
-rw-r--r-- | tools/niminst/EnvVarUpdate.nsh | 346 | ||||
-rw-r--r-- | tools/niminst/buildbat.nimf | 79 | ||||
-rw-r--r-- | tools/niminst/buildsh.nimf | 287 | ||||
-rw-r--r-- | tools/niminst/debcreation.nim | 239 | ||||
-rw-r--r-- | tools/niminst/deinstall.nimf | 81 | ||||
-rw-r--r-- | tools/niminst/inno.nimf | 182 | ||||
-rw-r--r-- | tools/niminst/install.nimf | 138 | ||||
-rw-r--r-- | tools/niminst/makefile.nimf | 219 | ||||
-rw-r--r-- | tools/niminst/nim-file.ico | bin | 0 -> 3311 bytes | |||
-rw-r--r-- | tools/niminst/niminst.nim | 751 | ||||
-rw-r--r-- | tools/niminst/nsis.nimf | 445 | ||||
-rw-r--r-- | tools/niminst/setup.ico | bin | 0 -> 2551 bytes | |||
-rw-r--r-- | tools/niminst/uninstall.ico | bin | 0 -> 2226 bytes |
13 files changed, 2767 insertions, 0 deletions
diff --git a/tools/niminst/EnvVarUpdate.nsh b/tools/niminst/EnvVarUpdate.nsh new file mode 100644 index 000000000..4340b8f3c --- /dev/null +++ b/tools/niminst/EnvVarUpdate.nsh @@ -0,0 +1,346 @@ +/** + * EnvVarUpdate.nsh + * : Environmental Variables: append, prepend, and remove entries + * + * WARNING: If you use StrFunc.nsh header then include it before this file + * with all required definitions. This is to avoid conflicts + * + * Usage: + * ${EnvVarUpdate} "ResultVar" "EnvVarName" "Action" "RegLoc" "PathString" + * + * Credits: + * Version 1.0 + * * Cal Turney (turnec2) + * * Amir Szekely (KiCHiK) and e-circ for developing the forerunners of this + * function: AddToPath, un.RemoveFromPath, AddToEnvVar, un.RemoveFromEnvVar, + * WriteEnvStr, and un.DeleteEnvStr + * * Diego Pedroso (deguix) for StrTok + * * Kevin English (kenglish_hi) for StrContains + * * Hendri Adriaens (Smile2Me), Diego Pedroso (deguix), and Dan Fuhry + * (dandaman32) for StrReplace + * + * Version 1.1 (compatibility with StrFunc.nsh) + * * techtonik + * + * http://nsis.sourceforge.net/Environmental_Variables:_append%2C_prepend%2C_and_remove_entries + * + */ + + +!ifndef ENVVARUPDATE_FUNCTION +!define ENVVARUPDATE_FUNCTION +!verbose push +!verbose 3 +!include "LogicLib.nsh" +!include "WinMessages.NSH" +!include "StrFunc.nsh" + +; ---- Fix for conflict if StrFunc.nsh is already includes in main file ----------------------- +!macro _IncludeStrFunction StrFuncName + !ifndef ${StrFuncName}_INCLUDED + ${${StrFuncName}} + !endif + !ifndef Un${StrFuncName}_INCLUDED + ${Un${StrFuncName}} + !endif + !define un.${StrFuncName} "${Un${StrFuncName}}" +!macroend + +!insertmacro _IncludeStrFunction StrTok +!insertmacro _IncludeStrFunction StrStr +!insertmacro _IncludeStrFunction StrRep + +; ---------------------------------- Macro Definitions ---------------------------------------- +!macro _EnvVarUpdateConstructor ResultVar EnvVarName Action Regloc PathString + Push "${EnvVarName}" + Push "${Action}" + Push "${RegLoc}" + Push "${PathString}" + Call EnvVarUpdate + Pop "${ResultVar}" +!macroend +!define EnvVarUpdate '!insertmacro "_EnvVarUpdateConstructor"' + +!macro _unEnvVarUpdateConstructor ResultVar EnvVarName Action Regloc PathString + Push "${EnvVarName}" + Push "${Action}" + Push "${RegLoc}" + Push "${PathString}" + Call un.EnvVarUpdate + Pop "${ResultVar}" +!macroend +!define un.EnvVarUpdate '!insertmacro "_unEnvVarUpdateConstructor"' +; ---------------------------------- Macro Definitions end------------------------------------- + +;----------------------------------- EnvVarUpdate start---------------------------------------- +!define hklm_all_users 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' +!define hkcu_current_user 'HKCU "Environment"' + +!macro EnvVarUpdate UN + +Function ${UN}EnvVarUpdate + + Push $0 + Exch 4 + Exch $1 + Exch 3 + Exch $2 + Exch 2 + Exch $3 + Exch + Exch $4 + Push $5 + Push $6 + Push $7 + Push $8 + Push $9 + Push $R0 + + /* After this point: + ------------------------- + $0 = ResultVar (returned) + $1 = EnvVarName (input) + $2 = Action (input) + $3 = RegLoc (input) + $4 = PathString (input) + $5 = Orig EnvVar (read from registry) + $6 = Len of $0 (temp) + $7 = tempstr1 (temp) + $8 = Entry counter (temp) + $9 = tempstr2 (temp) + $R0 = tempChar (temp) */ + + ; Step 1: Read contents of EnvVarName from RegLoc + ; + ; Check for empty EnvVarName + ${If} $1 == "" + SetErrors + DetailPrint "ERROR: EnvVarName is blank" + Goto EnvVarUpdate_Restore_Vars + ${EndIf} + + ; Check for valid Action + ${If} $2 != "A" + ${AndIf} $2 != "P" + ${AndIf} $2 != "R" + SetErrors + DetailPrint "ERROR: Invalid Action - must be A, P, or R" + Goto EnvVarUpdate_Restore_Vars + ${EndIf} + + ${If} $3 == HKLM + ReadRegStr $5 ${hklm_all_users} $1 ; Get EnvVarName from all users into $5 + ${ElseIf} $3 == HKCU + ReadRegStr $5 ${hkcu_current_user} $1 ; Read EnvVarName from current user into $5 + ${Else} + SetErrors + DetailPrint 'ERROR: Action is [$3] but must be "HKLM" or HKCU"' + Goto EnvVarUpdate_Restore_Vars + ${EndIf} + + ; Check for empty PathString + ${If} $4 == "" + SetErrors + DetailPrint "ERROR: PathString is blank" + Goto EnvVarUpdate_Restore_Vars + ${EndIf} + + Push $6 + Push $7 + Push $8 + StrLen $7 $4 + StrLen $6 $5 + IntOp $8 $6 + $7 + ${If} $5 == "" + ${OrIf} $8 >= ${NSIS_MAX_STRLEN} + SetErrors + DetailPrint "Current $1 length ($6) too long to modify in NSIS; set manually if needed" + Pop $8 + Pop $7 + Pop $6 + Goto EnvVarUpdate_Restore_Vars + ${EndIf} + Pop $8 + Pop $7 + Pop $6 + + ; Make sure we've got some work to do + ${If} $5 == "" + ${AndIf} $2 == "R" + SetErrors + DetailPrint "$1 is empty - Nothing to remove" + Goto EnvVarUpdate_Restore_Vars + ${EndIf} + + ; Step 2: Scrub EnvVar + ; + StrCpy $0 $5 ; Copy the contents to $0 + ; Remove spaces around semicolons (NOTE: spaces before the 1st entry or + ; after the last one are not removed here but instead in Step 3) + ${If} $0 != "" ; If EnvVar is not empty ... + ${Do} + ${${UN}StrStr} $7 $0 " ;" + ${If} $7 == "" + ${ExitDo} + ${EndIf} + ${${UN}StrRep} $0 $0 " ;" ";" ; Remove '<space>;' + ${Loop} + ${Do} + ${${UN}StrStr} $7 $0 "; " + ${If} $7 == "" + ${ExitDo} + ${EndIf} + ${${UN}StrRep} $0 $0 "; " ";" ; Remove ';<space>' + ${Loop} + ${Do} + ${${UN}StrStr} $7 $0 ";;" + ${If} $7 == "" + ${ExitDo} + ${EndIf} + ${${UN}StrRep} $0 $0 ";;" ";" + ${Loop} + + ; Remove a leading or trailing semicolon from EnvVar + StrCpy $7 $0 1 0 + ${If} $7 == ";" + StrCpy $0 $0 "" 1 ; Change ';<EnvVar>' to '<EnvVar>' + ${EndIf} + StrLen $6 $0 + IntOp $6 $6 - 1 + StrCpy $7 $0 1 $6 + ${If} $7 == ";" + StrCpy $0 $0 $6 ; Change ';<EnvVar>' to '<EnvVar>' + ${EndIf} + ; DetailPrint "Scrubbed $1: [$0]" ; Uncomment to debug + ${EndIf} + + /* Step 3. Remove all instances of the target path/string (even if "A" or "P") + $6 = bool flag (1 = found and removed PathString) + $7 = a string (e.g. path) delimited by semicolon(s) + $8 = entry counter starting at 0 + $9 = copy of $0 + $R0 = tempChar */ + + ${If} $5 != "" ; If EnvVar is not empty ... + StrCpy $9 $0 + StrCpy $0 "" + StrCpy $8 0 + StrCpy $6 0 + + ${Do} + ${${UN}StrTok} $7 $9 ";" $8 "0" ; $7 = next entry, $8 = entry counter + + ${If} $7 == "" ; If we've run out of entries, + ${ExitDo} ; were done + ${EndIf} ; + + ; Remove leading and trailing spaces from this entry (critical step for Action=Remove) + ${Do} + StrCpy $R0 $7 1 + ${If} $R0 != " " + ${ExitDo} + ${EndIf} + StrCpy $7 $7 "" 1 ; Remove leading space + ${Loop} + ${Do} + StrCpy $R0 $7 1 -1 + ${If} $R0 != " " + ${ExitDo} + ${EndIf} + StrCpy $7 $7 -1 ; Remove trailing space + ${Loop} + ${If} $7 == $4 ; If string matches, remove it by not appending it + StrCpy $6 1 ; Set 'found' flag + ${ElseIf} $7 != $4 ; If string does NOT match + ${AndIf} $0 == "" ; and the 1st string being added to $0, + StrCpy $0 $7 ; copy it to $0 without a prepended semicolon + ${ElseIf} $7 != $4 ; If string does NOT match + ${AndIf} $0 != "" ; and this is NOT the 1st string to be added to $0, + StrCpy $0 $0;$7 ; append path to $0 with a prepended semicolon + ${EndIf} ; + + IntOp $8 $8 + 1 ; Bump counter + ${Loop} ; Check for duplicates until we run out of paths + ${EndIf} + + ; Step 4: Perform the requested Action + ; + ${If} $2 != "R" ; If Append or Prepend + ${If} $6 == 1 ; And if we found the target + DetailPrint "Target is already present in $1. It will be removed and" + ${EndIf} + ${If} $0 == "" ; If EnvVar is (now) empty + StrCpy $0 $4 ; just copy PathString to EnvVar + ${If} $6 == 0 ; If found flag is either 0 + ${OrIf} $6 == "" ; or blank (if EnvVarName is empty) + DetailPrint "$1 was empty and has been updated with the target" + ${EndIf} + ${ElseIf} $2 == "A" ; If Append (and EnvVar is not empty), + StrCpy $0 $0;$4 ; append PathString + ${If} $6 == 1 + DetailPrint "appended to $1" + ${Else} + DetailPrint "Target was appended to $1" + ${EndIf} + ${Else} ; If Prepend (and EnvVar is not empty), + StrCpy $0 $4;$0 ; prepend PathString + ${If} $6 == 1 + DetailPrint "prepended to $1" + ${Else} + DetailPrint "Target was prepended to $1" + ${EndIf} + ${EndIf} + ${Else} ; If Action = Remove + ${If} $6 == 1 ; and we found the target + DetailPrint "Target was found and removed from $1" + ${Else} + DetailPrint "Target was NOT found in $1 (nothing to remove)" + ${EndIf} + ${If} $0 == "" + DetailPrint "$1 is now empty" + ${EndIf} + ${EndIf} + + ; Step 5: Update the registry at RegLoc with the updated EnvVar and announce the change + ; + ClearErrors + ${If} $3 == HKLM + WriteRegExpandStr ${hklm_all_users} $1 $0 ; Write it in all users section + ${ElseIf} $3 == HKCU + WriteRegExpandStr ${hkcu_current_user} $1 $0 ; Write it to current user section + ${EndIf} + + IfErrors 0 +4 + MessageBox MB_OK|MB_ICONEXCLAMATION "Could not write updated $1 to $3" + DetailPrint "Could not write updated $1 to $3" + Goto EnvVarUpdate_Restore_Vars + + ; "Export" our change + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + EnvVarUpdate_Restore_Vars: + ; + ; Restore the user's variables and return ResultVar + Pop $R0 + Pop $9 + Pop $8 + Pop $7 + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Push $0 ; Push my $0 (ResultVar) + Exch + Pop $0 ; Restore his $0 + +FunctionEnd + +!macroend ; EnvVarUpdate UN +!insertmacro EnvVarUpdate "" +!insertmacro EnvVarUpdate "un." +;----------------------------------- EnvVarUpdate end---------------------------------------- + +!verbose pop +!endif diff --git a/tools/niminst/buildbat.nimf b/tools/niminst/buildbat.nimf new file mode 100644 index 000000000..6767461e3 --- /dev/null +++ b/tools/niminst/buildbat.nimf @@ -0,0 +1,79 @@ +#? stdtmpl(subsChar='?') | standard +#proc generateBuildBatchScript(c: ConfigData, winIndex, cpuIndex32, cpuIndex64: int): string = +# result = "@echo off\nREM Generated by niminst\n" +SET CC=gcc +SET LINKER=gcc +SET COMP_FLAGS=?{c.ccompiler.flags} +SET LINK_FLAGS=?{c.linker.flags} +SET BIN_DIR=?{firstBinPath(c).toWin} + +REM Detect gcc arch +IF DEFINED ARCH ( + ECHO Forcing %CC% arch +) ELSE ( + ECHO Detecting %CC% arch + ECHO int main^(^) { return sizeof^(void *^); } | gcc -xc - -o archtest && archtest + IF ERRORLEVEL 4 SET ARCH=32 + IF ERRORLEVEL 8 SET ARCH=64 + del archtest.* +) +ECHO Building with %ARCH% bit %CC% + +if EXIST ..\koch.nim SET BIN_DIR=..\bin + +if NOT EXIST %BIN_DIR%\nul mkdir %BIN_DIR% + +REM call the compiler: + +IF %ARCH% EQU 32 ( + +# block win32: +# var linkCmd = "" +# if cpuIndex32 != -1: +# for ff in items(c.cfiles[winIndex][cpuIndex32]): +# let f = ff.toWin + ECHO %CC% %COMP_FLAGS% -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")} + CALL %CC% %COMP_FLAGS% -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")} +# linkCmd.add(" " & changeFileExt(f, "o")) + IF ERRORLEVEL 1 (GOTO:END) +# end for +# end if + + ECHO %LINKER% -o ?{"%BIN_DIR%"\toLowerAscii(c.name)}.exe ?linkCmd %LINK_FLAGS% + CALL %LINKER% -o ?{"%BIN_DIR%"\toLowerAscii(c.name)}.exe ?linkCmd %LINK_FLAGS% + +# end block + +) ELSE IF %ARCH% EQU 64 ( + +# block win64: +# var linkCmd = "" +# if cpuIndex64 != -1: +# for ff in items(c.cfiles[winIndex][cpuIndex64]): +# let f = ff.toWin + ECHO %CC% %COMP_FLAGS% -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")} + CALL %CC% %COMP_FLAGS% -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")} +# linkCmd.add(" " & changeFileExt(f, "o")) + IF ERRORLEVEL 1 (GOTO:END) +# end for +# end if + + ECHO %LINKER% -o ?{"%BIN_DIR%"\toLowerAscii(c.name)}.exe ?linkCmd %LINK_FLAGS% + CALL %LINKER% -o ?{"%BIN_DIR%"\toLowerAscii(c.name)}.exe ?linkCmd %LINK_FLAGS% + +# end block +) + +:END +IF ERRORLEVEL 1 ( + ECHO FAILURE + ECHO. + ECHO CSource compilation failed. Please check that the gcc compiler is in + ECHO the PATH environment variable, and that you are calling the batch script + ECHO that matches the target architecture of the compiler. + ECHO. + ECHO Use build.bat to autodetect the compiler architecture. +) ELSE ( + ECHO SUCCESS +) +exit /b %ERRORLEVEL% diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf new file mode 100644 index 000000000..063a02779 --- /dev/null +++ b/tools/niminst/buildsh.nimf @@ -0,0 +1,287 @@ +#? stdtmpl(subsChar='?') | standard +#proc generateBuildShellScript(c: ConfigData): string = +# result = "#!/bin/sh\n# Generated from niminst\n" & +# "# Template is in tools/niminst/buildsh.nimf\n" & +# "# To regenerate run ``niminst csource`` or ``koch csource``\n" + +set -e + +while : +do + case "$1" in + --os) + optos=$2 + shift 2 + ;; + --cpu) + optcpu=$2 + shift 2 + ;; + --osname) + optosname=$2 + shift 2 + ;; + --parallel) + parallel=$2 + shift 2 + ;; + --extraBuildArgs) + extraBuildArgs=" $2" + shift 2 + ;; + -h | --help) + echo "Options:" + echo " --os <OS>" + echo " --cpu <CPU architecture>" + echo " --osname <name> Additional OS specification (used for Android)" + echo " --extraBuildArgs <args> Additional arguments passed to the compiler" + echo " --parallel <number> Multiprocess build. Requires GNU parallel" + exit 0 + ;; + --) # End of all options + shift + break; + ;; + -*) + echo 2>&1 "Error: Unknown option: $1" >&2 + exit 1 + ;; + *) # No more options + break + ;; + esac +done + +parallel="${parallel:-0}" +CC="${CC:-gcc}" +if [ "$parallel" -gt 1 ]; then + if ! command -v sem > /dev/null; then + echo "Error: GNU parallel is required to use --parallel" + exit 1 + fi + CC="sem -j $parallel --id $$ ${CC}" +fi +COMP_FLAGS="${CPPFLAGS:-} ${CFLAGS:-} ?{c.ccompiler.flags}$extraBuildArgs" +LINK_FLAGS="${LDFLAGS:-} ?{c.linker.flags}" +PS4="" +# add(result, "# platform detection\n") +ucpu=`uname -m` +uos=`uname` +uosname= +# add(result, "# bin dir detection\n") +binDir=?{firstBinPath(c).toUnix} + +if [ -s ../koch.nim ]; then + binDir="../bin" +fi + +if [ ! -d $binDir ]; then + mkdir $binDir +fi + +# add(result, "# override OS, CPU and OS Name with command-line arguments\n") +if [ -n "$optos" ]; then + uos="$optos" +fi +if [ -n "$optcpu" ]; then + ucpu="$optcpu" +fi +if [ -n "$optcpu" ]; then + uosname="$optosname" +fi + +# add(result, "# convert to lower case:\n") +ucpu=`echo $ucpu | tr "[:upper:]" "[:lower:]"` +uos=`echo $uos | tr "[:upper:]" "[:lower:]"` +uosname=`echo $uosname | tr "[:upper:]" "[:lower:]"` + +case $uos in + *linux* ) + myos="linux" + LINK_FLAGS="$LINK_FLAGS -ldl -lm -lrt" + ;; + *dragonfly* ) + myos="dragonfly" + LINK_FLAGS="$LINK_FLAGS -lm" + ;; + *freebsd* ) + myos="freebsd" + CC="clang" + LINK_FLAGS="$LINK_FLAGS -lm" + ;; + *crossos* ) + myos="crossos" + CC="clang" + LINK_FLAGS="$LINK_FLAGS -lm" + ;; + *openbsd* ) + myos="openbsd" + CC="clang" + LINK_FLAGS="$LINK_FLAGS -lm" + ;; + *netbsd* ) + myos="netbsd" + LINK_FLAGS="$LINK_FLAGS -lm" + ucpu=`uname -p` + ;; + *darwin* ) + myos="macosx" + CC="clang" + LINK_FLAGS="$LINK_FLAGS -ldl -lm" + if [ "$HOSTTYPE" = "x86_64" ] ; then + ucpu="amd64" + fi + ;; + *aix* ) + myos="aix" + LINK_FLAGS="$LINK_FLAGS -ldl -lm" + ;; + *solaris* | *sun* ) + myos="solaris" + LINK_FLAGS="$LINK_FLAGS -ldl -lm -lsocket -lnsl" + ;; + *SunOS* ) + myos="solaris" + LINK_FLAGS="$LINK_FLAGS -ldl -lm -lsocket -lnsl" + isOpenIndiana="yes" + ;; + *haiku* ) + myos="haiku" + LINK_FLAGS="$LINK_FLAGS -lroot -lnetwork" + ;; + *mingw* | *msys* ) + myos="windows" + ;; + *android* ) + myos="android" + LINK_FLAGS="$LINK_FLAGS -ldl -lm -lrt" + LINK_FLAGS="$LINK_FLAGS -landroid-glob" + ;; + *) + echo 2>&1 "Error: unknown operating system: $uos" + exit 1 + ;; +esac + +case $ucpu in + *i386* | *i486* | *i586* | *i686* | *bepc* | *i86pc* ) + if [ "$isOpenIndiana" = "yes" ] || [ `uname -o` == "illumos" ] ; then + mycpu="amd64" + else + mycpu="i386" + fi + ;; + *amd*64* | *x86-64* | *x86_64* ) + mycpu="amd64" ;; + *sparc*|*sun* ) + mycpu="sparc" + if [ "$myos" = "linux" ] ; then + if [ "$(getconf LONG_BIT)" = "64" ]; then + mycpu="sparc64" + elif [ "$(isainfo -b)" = "64" ]; then + mycpu="sparc64" + fi + fi + ;; + *ppc64le* ) + mycpu="powerpc64el" ;; + *ppc64* ) + if [ "$myos" = "linux" ] ; then + COMP_FLAGS="$COMP_FLAGS -m64" + LINK_FLAGS="$LINK_FLAGS -m64" + fi + mycpu="powerpc64" ;; + *power*|*ppc* ) + if [ "$myos" = "freebsd" ] ; then + if [ "$ucpu" != "powerpc" ] ; then + COMP_FLAGS="$COMP_FLAGS -m64" + LINK_FLAGS="$LINK_FLAGS -m64" + fi + mycpu=`uname -p` + case $mycpu in + powerpc64le) + mycpu="powerpc64el" + esac + else + mycpu="powerpc" + fi + ;; + *hppa*) + mycpu="hppa" ;; + *ia64*) + mycpu="ia64" ;; + *m68k*) + mycpu="m68k" ;; + *mips* ) + mycpu="$("$CC" -dumpmachine | sed 's/-.*//')" + case $mycpu in + mips|mipsel|mips64|mips64el) + ;; + *) + echo 2>&1 "Error: unknown MIPS target: $mycpu" + exit 1 + esac + ;; + *alpha* ) + mycpu="alpha" ;; + *aarch64*|*arm64* ) + mycpu="arm64" ;; + *arm*|*armv6l*|*armv7l*|*armv8l* ) + mycpu="arm" ;; + *riscv64|riscv* ) + mycpu="riscv64" ;; + *e2k* ) + mycpu="e2k" ;; + *loongarch64* ) + mycpu="loongarch64" ;; + *) + echo 2>&1 "Error: unknown processor: $ucpu" + exit 1 + ;; +esac + +case $uosname in + *android* ) + LINK_FLAGS="$LINK_FLAGS -landroid-glob" + myosname="android" + myos="android" + ;; +esac + +# add(result, "# call the compiler:\n") +echo \# OS: $myos +echo \# CPU: $mycpu + +case $myos in +# for osA in 1..c.oses.len: +?{c.oses[osA-1]}) + case $mycpu in +# for cpuA in 1..c.cpus.len: + ?{c.cpus[cpuA-1]}) + set -x +# var linkCmd = "" +# for ff in items(c.cfiles[osA][cpuA]): +# let f = ff.toUnix + $CC $COMP_FLAGS -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")} +# add(linkCmd, " \\\n" & changeFileExt(f, "o")) +# end for + if [ "$parallel" -gt 0 ]; then + sem --wait --id $$ + fi + $CC -o ?{"$binDir/" & toLowerAscii(c.name)} ?linkCmd $LINK_FLAGS + ;; +# end for + *) + echo 2>&1 "Error: no C code generated for: [$myos: $mycpu]" + exit 1 + ;; + esac + ;; +# end for +*) + echo 2>&1 "Error: no C code generated for: [$myos: $mycpu]" + exit 1 + ;; +esac + +: SUCCESS diff --git a/tools/niminst/debcreation.nim b/tools/niminst/debcreation.nim new file mode 100644 index 000000000..219cb44ce --- /dev/null +++ b/tools/niminst/debcreation.nim @@ -0,0 +1,239 @@ +# +# +# The Nim Installation Generator +# (c) Copyright 2012 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import osproc, times, os, strutils + + +when defined(nimPreviewSlimSystem): + import std/[assertions, syncio] + +# http://www.debian.org/doc/manuals/maint-guide/ + +# Required files for debhelper. +# -- control +# -- copyright +# -- changelog +# -- rules + +type + TDebOptions* = object + buildDepends*, pkgDepends*, shortDesc*: string + licenses*: seq[tuple[files, license: string]] + +template addN(r: string) = + result.add(r) + result.add("\n") + +proc createControl(pkgName, maintainer, shortDesc, desc: string, + buildDepends, pkgDepends: string = ""): string = + ## pkgName: Should be the package name, no spaces. + ## maintainer: firstName lastName <email> + ## shortDesc: short description of the application + ## desc: long description of the application + ## buildDepends: what the build depends on (compiling from source), + ## this needs to be in the format deb accepts. For example, + ## for gcc: ``gcc (>= 4:4.3.2)`` + ## Multiple dependencies should be separated by commas. + ## pkgDepends: Same as buildDepends except that this specifies the + ## dependencies that the compiled application depends on. + + + result = "" + + addN("Source: " & pkgName) + addN("Maintainer: " & maintainer) + addN("Section: misc") + addN("Priority: optional") + addN("Standards-Version: 3.9.2") + addN("Build-Depends: debhelper (>= 8)" & + (if buildDepends != "": ", " & buildDepends else: "")) + addN("\n") + addN("Package: " & pkgName) + addN("Architecture: any") + addN("Depends: ${shlibs:Depends}, ${misc:Depends}" & + (if pkgDepends != "": ", " & pkgDepends else: "")) + + var formattedDesc = "" + for line in splitLines(desc): + if line == "": + formattedDesc.add(" .\n") + else: + formattedDesc.add(" " & line & "\n") + + addN("Description: " & shortDesc & "\n" & formattedDesc) + +proc createCopyright(pkgName, mtnName, mtnEmail, version: string, + licenses: seq[tuple[files, license: string]]): string = + ## pkgName: Package name + ## mtnName: Maintainer name + ## mtnEmail: Maintainer email + ## version: package version + ## licenses: files: This specifies the files that the `license` covers, + ## for example, it might be ``lib/*`` to cover the whole ``lib`` dir + ## license: This specifies the license, for example gpl2, or lgpl. + + result = "" + addN("Maintainer name: " & mtnName) + addN("Email-Address: " & mtnEmail) + addN("Date: " & $getTime()) + addN("Package Name: " & pkgName) + addN("Version: " & version) + for f, license in items(licenses): + addN("Files: " & f) + addN("License: " & license) + +proc formatDateTime(t: DateTime, timezone: string): string = + var day = ($t.weekday)[0..2] & ", " + + return "$1$2 $3 $4 $5:$6:$7 $8" % [day, intToStr(t.monthday, 2), + ($t.month)[0..2], $t.year, intToStr(t.hour, 2), intToStr(t.minute, 2), + intToStr(t.second, 2), timezone] + +proc createChangelog(pkgName, version, maintainer: string): string = + ## pkgName: package name + ## version: package version + ## maintainer: firstName lastName <email> + result = "" + addN(pkgName & " (" & version & "-1) unstable; urgency=low") + addN("") + addN(" * Initial release.") + addN("") + addN(" -- " & maintainer & " " & + formatDateTime(utc(getTime()), "+0000")) + +proc createRules(): string = + ## Creates a nim application-agnostic rules file for building deb packages. + ## Please note: this assumes the c sources have been built and the + ## ``build.sh`` and ``install.sh`` files are available. + result = "" + addN("#!/usr/bin/make -f") + addN("%:") + addN("\tdh $@\n") + addN("dh_install:") + addN("\tdh_install --sourcedir=debian/tmp") + addN("override_dh_auto_clean:") + addN("\tfind . -name *.o -exec rm {} \\;") + addN("override_dh_auto_build:") + addN("\t./build.sh") + addN("override_dh_auto_install:") + addN("\t./install.sh debian/tmp") + +proc createIncludeBinaries(binaries: seq[string]): string = + return join(binaries, "\n") + +proc createDotInstall(pkgName: string, binaries, config, docs, + lib: seq[string]): string = + result = "" + for b in binaries: + addN(pkgName / b & " " & "usr/bin/") + for c in config: + addN(pkgName / c & " " & "etc/") + for d in docs: + addN(pkgName / d & " " & "usr/share/doc/nim/") + for l1 in lib: + addN(pkgName / l1 & " " & "usr/lib/nim") + +proc makeMtn(name, email: string): string = + return name & " <" & email & ">" + +proc assertSuccess(exitCode: int) = + doAssert(exitCode == QuitSuccess) + +proc prepDeb*(packName, version, mtnName, mtnEmail, shortDesc, desc: string, + licenses: seq[tuple[files, license: string]], binaries, + config, docs, lib: seq[string], + buildDepends, pkgDepends = "") = + ## binaries/config/docs/lib: files relative to nim's root, that need to + ## be installed. + + let pkgName = packName.toLowerAscii() + + var workingDir = getTempDir() / "niminst" / "deb" + var upstreamSource = (pkgName & "-" & version) + + echo("Making sure build.sh and install.sh are +x") + assertSuccess execCmd("chmod +x \"" & + (workingDir / upstreamSource / "build.sh") & "\"") + assertSuccess execCmd("chmod +x \"" & + (workingDir / upstreamSource / "install.sh") & "\"") + + var tarCmd = "tar pczf \"" & + (pkgName & "_" & version & ".orig.tar.gz") & + "\" \"" & upstreamSource & "\"" + echo(tarCmd) + assertSuccess execCmd("cd \"" & workingDir & "\" && " & tarCmd) + + echo("Creating necessary files in debian/") + createDir(workingDir / upstreamSource / "debian") + + template writeDebian(f, s: string) = + writeFile(workingDir / upstreamSource / "debian" / f, s) + + var controlFile = createControl(pkgName, makeMtn(mtnName, mtnEmail), + shortDesc, desc, buildDepends, pkgDepends) + echo("debian/control") + writeDebian("control", controlFile) + + var copyrightFile = createCopyright(pkgName, mtnName, mtnEmail, version, + licenses) + echo("debian/copyright") + writeDebian("copyright", copyrightFile) + + var changelogFile = createChangelog(pkgName, version, + makeMtn(mtnName, mtnEmail)) + echo("debian/changelog") + writeDebian("changelog", changelogFile) + + echo("debian/rules") + writeDebian("rules", createRules()) + + echo("debian/compat") + writeDebian("compat", "8") + + echo("debian/" & pkgName & ".install") + writeDebian(pkgName & ".install", + createDotInstall(pkgName, binaries, config, docs, lib)) + + # Other things.. + createDir(workingDir / upstreamSource / "debian" / "source") + echo("debian/source/format") + writeDebian("source" / "format", + "3.0 (quilt)") + echo("debian/source/include-binaries") + writeFile(workingDir / upstreamSource / "debian" / "source" / "include-binaries", + createIncludeBinaries(binaries)) + + echo("All done, you can now build.") + echo("Before you do however, make sure the files in " & + workingDir / upstreamSource / "debian" & " are correct.") + echo("Change your directory to: " & workingDir / upstreamSource) + echo("And execute `debuild -us -uc` to build the .deb") + +when isMainModule: + #var controlFile = createControl("nim", "Dominik Picheta <morfeusz8@gmail.com>", + # "The Nim compiler", "Compiler for the Nim programming language", "gcc (>= 4:4.3.2)", "gcc (>= 4:4.3.2)") + + #echo(controlFile) + + #var copyrightFile = createCopyright("nim", "Dominik Picheta", "morfeusz8@a.b", "0.8.14", + # @[("bin/nim", "gpl2"), ("lib/*", "lgpl")]) + + #echo copyrightFile + + #var changelogFile = createChangelog("nim", "0.8.14", "Dom P <m@b.c>") + #echo(changelogFile) + + #echo(createRules()) + + prepDeb("nim", "0.9.2", "Dominik Picheta", "morfeusz8@gmail.com", + "The Nim compiler", "Compiler for the Nim programming language", + @[("bin/nim", "MIT"), ("lib/*", "MIT")], + @["bin/nim"], @["config/*"], @["doc/*"], @["lib/*"], + "gcc (>= 4:4.3.2)", "gcc (>= 4:4.3.2)") + diff --git a/tools/niminst/deinstall.nimf b/tools/niminst/deinstall.nimf new file mode 100644 index 000000000..0dcfda75e --- /dev/null +++ b/tools/niminst/deinstall.nimf @@ -0,0 +1,81 @@ +#? stdtmpl(subsChar='?') | standard +#proc generateDeinstallScript(c: ConfigData): string = +# result = "#!/bin/sh\n# Generated by niminst\n" +# var proj = c.name.toLowerAscii + +if [ $# -eq 1 ] ; then + case $1 in + "--help"|"-h"|"help"|"h") + echo "?c.displayName deinstallation script" + echo "Usage: [sudo] sh deinstall.sh DIR" + echo "Where DIR may be:" + echo " /usr/bin" + echo " /usr/local/bin" + echo " /opt" + echo " <some other dir> (treated similar '/opt')" + exit 1 + ;; + "/usr/bin") + bindir=/usr/bin + configdir=/etc/?proj + libdir=/usr/lib/?proj + docdir=/usr/share/?proj/doc + datadir=/usr/share/?proj/data + nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version" + ;; + "/usr/local/bin") + bindir=/usr/local/bin + configdir=/etc/?proj + libdir=/usr/local/lib/?proj + docdir=/usr/local/share/?proj/doc + datadir=/usr/local/share/?proj/data + nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version" + ;; + "/opt") + bindir="/opt/?proj/bin" + configdir="/opt/?proj/config" + libdir="/opt/?proj/lib" + docdir="/opt/?proj/doc" + datadir="/opt/?proj/data" + nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version" + ;; + *) + bindir="$1/?proj/bin" + configdir="$1/?proj/config" + libdir="$1/?proj/lib" + docdir="$1/?proj/doc" + datadir="$1/?proj/data" + nimbleDir="$1/?proj" + ;; + esac + echo "removing files..." + +#for ff in items(c.cat[fcUnixBin]): + #let f = ff.toUnix + rm -f $bindir/?f.skipRoot +#end for +#for ff in items(c.cat[fcConfig]): + #let f = ff.toUnix + rm -f $configdir/?f.skipRoot +#end for + rm -rf $docdir + rm -rf $datadir + rm -rf $libdir + + ## Nimble pkg stuff + #for f in items(c.cat[fcNimble]): + rm -f $nimbleDir/?f.toUnix + #end for + rm -f $nimbleDir/?{c.nimblePkgName}.nimble + + echo "deinstallation successful" +else + echo "?c.displayName deinstallation script" + echo "Usage: [sudo] sh deinstall.sh DIR" + echo "Where DIR may be:" + echo " /usr/bin" + echo " /usr/local/bin" + echo " /opt" + echo " <some other dir> (treated similar '/opt')" + exit 1 +fi diff --git a/tools/niminst/inno.nimf b/tools/niminst/inno.nimf new file mode 100644 index 000000000..ef2da8a75 --- /dev/null +++ b/tools/niminst/inno.nimf @@ -0,0 +1,182 @@ +#? stdtmpl | standard +#proc generateInnoSetup(c: ConfigData): string = +# result = "" +; Default Template for NimInst +[Setup] +AppName=$c.displayName +AppVerName=$c.displayName $c.version +DefaultDirName={code:GiveMeAPath|$c.displayName} +DefaultGroupName=$c.displayName +AllowNoIcons=yes +LicenseFile=${expandFilename(c.license)} +OutputBaseFilename=${c.name}_${c.version} +Compression=lzma +SolidCompression=yes +PrivilegesRequired=none +ChangesEnvironment=yes + +[Languages] +Name: english; MessagesFile: compiler:Default.isl + +[Files] + #for i in low(FileCategory)..fcWindows: + # for f in items(c.cat[i]): +Source: ${expandFilename(f).toWin}; DestDir: {app}\${splitFile(f).dir.toWin}; Flags: ignoreversion + # end for + #end for + +[Icons] + #if c.app == appConsole: +Name: {group}\Console for $c.displayName; Filename: {cmd} + #else: +Name: {group}\$c.displayName; Filename: {app}\${c.name}.exe + #end if + #for f in items(c.cat[fcDocStart]): +Name: {group}\Documentation; Filename: {app}\${f.toWin} + #end for +Name: {group}\{cm:UninstallProgram,$c.displayName}; Filename: {uninstallexe} + + #if c.binPaths.len > 0: +[Tasks] +Name: modifypath; Description: &Add $c.displayName to your system path (if not in path already); + #end if + +[Code] +function GiveMeAPath(const DefaultPathName: string): string; +begin + if IsAdminLoggedOn then result := ExpandConstant('{pf}') + else result := ExpandConstant('{userdocs}'); + result := result + '\' + DefaultPathName; +end; + + #if c.binPaths.len > 0: +// ---------------------------------------------------------------------------- +// +// Inno Setup Ver: 5.2.1 +// Script Version: 1.3.1 +// Author: Jared Breland <jbreland@legroom.net> +// Homepage: http://www.legroom.net/software +// +// Script Function: +// Enable modification of system path directly from Inno Setup installers + +function ModPathDir(): TArrayOfString; +begin + setArrayLength(result, $c.binPaths.len); + #var i = 0 + #for b in items(c.binPaths): + result[$i] := ExpandConstant('{app}') + '\${b.toWin}'; + #inc(i) + #end for +end; + +procedure ModPath(); +var + oldpath, newpath, aExecFile: String; + pathArr, aExecArr, pathdir: TArrayOfString; + i, d: Integer; +begin + // Get array of new directories and act on each individually + pathdir := ModPathDir(); + for d := 0 to GetArrayLength(pathdir)-1 do begin + // Modify WinNT path + if UsingWinNT() then begin + // Get current path, split into an array + RegQueryStringValue(HKEY_LOCAL_MACHINE, + 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', + 'Path', oldpath); + oldpath := oldpath + ';'; + i := 0; + while (Pos(';', oldpath) > 0) do begin + SetArrayLength(pathArr, i+1); + pathArr[i] := Copy(oldpath, 0, Pos(';', oldpath)-1); + oldpath := Copy(oldpath, Pos(';', oldpath)+1, Length(oldpath)); + i := i + 1; + // Check if current directory matches app dir + if pathdir[d] = pathArr[i-1] then begin + // if uninstalling, remove dir from path + if IsUninstaller() then continue + // if installing, abort because dir was already in path + else abort; + end; + // Add current directory to new path + if i = 1 then newpath := pathArr[i-1] + else newpath := newpath + ';' + pathArr[i-1]; + end; + // Append app dir to path if not already included + if not IsUninstaller() then + newpath := newpath + ';' + pathdir[d]; + // Write new path + RegWriteStringValue(HKEY_LOCAL_MACHINE, + 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', + 'Path', newpath); + end + else begin + // Modify Win9x path + // Convert to shortened dirname + pathdir[d] := GetShortName(pathdir[d]); + // If autoexec.bat exists, check if app dir already exists in path + aExecFile := 'C:\AUTOEXEC.BAT'; + if FileExists(aExecFile) then begin + LoadStringsFromFile(aExecFile, aExecArr); + for i := 0 to GetArrayLength(aExecArr)-1 do begin + if not IsUninstaller() then begin + // If app dir already exists while installing, abort add + if (Pos(pathdir[d], aExecArr[i]) > 0) then + abort; + end + else begin + // If app dir exists and = what we originally set, + // then delete at uninstall + if aExecArr[i] = 'SET PATH=%PATH%;' + pathdir[d] then + aExecArr[i] := ''; + end; + end; + end; + // If app dir not found, or autoexec.bat didn't exist, then + // (create and) append to current path + if not IsUninstaller() then begin + SaveStringToFile(aExecFile, #13#10 + 'SET PATH=%PATH%;' + pathdir[d], + True); + end + else begin + // If uninstalling, write the full autoexec out + SaveStringsToFile(aExecFile, aExecArr, False); + end; + end; + + // Write file to flag modifypath was selected + // Workaround since IsTaskSelected() cannot be called at uninstall and + // AppName and AppId cannot be "read" in Code section + if not IsUninstaller() then + SaveStringToFile(ExpandConstant('{app}') + '\uninsTasks.txt', + WizardSelectedTasks(False), False); + end; +end; + +procedure CurStepChanged(CurStep: TSetupStep); +begin + if CurStep = ssPostInstall then begin + if IsTaskSelected('modifypath') then + ModPath(); + end +end; + +procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); +var + appdir, selectedTasks: String; +begin + appdir := ExpandConstant('{app}'); + if CurUninstallStep = usUninstall then begin + if LoadStringFromFile(appdir + '\uninsTasks.txt', selectedTasks) then + if Pos('modifypath', selectedTasks) > 0 then + ModPath(); + DeleteFile(appdir + '\uninsTasks.txt') + end; +end; + +function NeedRestart(): Boolean; +begin + result := IsTaskSelected('modifypath') and not UsingWinNT() +end; + #end if diff --git a/tools/niminst/install.nimf b/tools/niminst/install.nimf new file mode 100644 index 000000000..75ff9ce11 --- /dev/null +++ b/tools/niminst/install.nimf @@ -0,0 +1,138 @@ +#? stdtmpl(subsChar = '?') | standard +#proc generateInstallScript(c: ConfigData): string = +# result = "#!/bin/sh\n# Generated by niminst\n" +# var proj = c.name.toLowerAscii + +set -e + +if [ $# -eq 1 ] ; then +#if c.cat[fcUnixBin].len > 0: + if [ -f "?{c.cat[fcUnixBin][0].toUnix}" ] + then + echo "?c.displayName build detected" + else + echo "Please build ?c.displayName before installing it" + exit 1 + fi +#end if + case $1 in + "--help"|"-h"|"help"|"h") + echo "?c.displayName installation script" + echo "Usage: [sudo] [env DESTDIR=...] sh install.sh DIR" + echo "Where DIR may be:" + echo " /usr/bin" + echo " /usr/local/bin" + echo " /opt" + echo " <some other dir> (treated similar to '/opt')" + echo "To deinstall, use the command:" + echo "sh deinstall.sh DIR" + exit 1 + ;; + "/usr/bin") + bindir=$1 + configdir="/etc/?proj" + libdir="/usr/lib/?proj" + docdir="/usr/share/?proj/doc" + datadir="/usr/share/?proj/data" + nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version" + ;; + "/usr/local/bin") + bindir=$1 + configdir="/etc/?proj" + libdir="/usr/local/lib/?proj" + docdir="/usr/local/share/?proj/doc" + datadir="/usr/local/share/?proj/data" + nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version" + ;; + "/opt") + bindir="/opt/?proj/bin" + configdir="/opt/?proj/config" + libdir="/opt/?proj/lib" + docdir="/opt/?proj/doc" + datadir="/opt/?proj/data" + nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version" + ;; + *) + bindir="$1/?proj/bin" + configdir="$1/?proj/config" + libdir="$1/?proj/lib" + docdir="$1/?proj/doc" + datadir="$1/?proj/data" + nimbleDir="$1/?proj" + ;; + esac + + bindir="${DESTDIR}${bindir}" + configdir="${DESTDIR}${configdir}" + libdir="${DESTDIR}${libdir}" + docdir="${DESTDIR}${docdir}" + datadir="${DESTDIR}${datadir}" + nimbleDir="${DESTDIR}${nimbleDir}" + + mkdir -p "$bindir" + mkdir -p "$configdir" + mkdir -p "$libdir" + mkdir -p "$docdir" + mkdir -p "$datadir" + mkdir -p "$nimbleDir" + echo "copying files..." +#var createdDirs = newStringTable() +#for cat in {fcConfig..fcLib, fcNimble}: +# for f in items(c.cat[cat]): +# var mk = splitFile(f.skipRoot).dir +# if cat != fcNimble: +# mk = unixDirVars[cat] & "/" & mk +# else: +# mk = "$nimbleDir" / splitFile(f).dir +# end if +# if mk.len > 0 and not createdDirs.hasKey(mk): +# createdDirs[mk] = "true" + mkdir -p "?{mk.toUnix}" +# end if +# end for +#end for + +#for f in items(c.cat[fcUnixBin]): + cp "?f.toUnix" "$bindir/?f.skipRoot.toUnix" + chmod 755 "$bindir/?f.skipRoot.toUnix" +#end for +#for f in items(c.cat[fcConfig]): + cp "?f.toUnix" "$configdir/?f.skipRoot.toUnix" + chmod 644 "$configdir/?f.skipRoot.toUnix" +#end for +#for f in items(c.cat[fcData]): + if [ -f "?f.toUnix" ]; then + cp "?f.toUnix" "$datadir/?f.skipRoot.toUnix" + chmod 644 "$datadir/?f.skipRoot.toUnix" + fi +#end for +#for f in items(c.cat[fcDoc]): + if [ -f "?f.toUnix" ]; then + cp "?f.toUnix" "$docdir/?f.skipRoot.toUnix" + chmod 644 "$docdir/?f.skipRoot.toUnix" + fi +#end for +#for f in items(c.cat[fcLib]): + cp "?f.toUnix" "$libdir/?f.skipRoot.toUnix" + chmod 644 "$libdir/?f.skipRoot.toUnix" +#end for +#for f in items(c.cat[fcNimble]): + cp "?f.toUnix" "$nimbleDir/?f.toUnix" + chmod 644 "$nimbleDir/?f.toUnix" +#end for +cp "?{c.nimblePkgName}.nimble" "$nimbleDir/?{c.nimblePkgName}.nimble" +chmod 644 "$nimbleDir/?{c.nimblePkgName}.nimble" + + echo "installation successful" +else + echo "?c.displayName installation script" + echo "Usage: [sudo] [env DESTDIR=...] sh install.sh DIR" + echo "Where DIR may be:" + echo " /usr/bin" + echo " /usr/local/bin" + echo " /opt" + echo " <some other dir> (treated similar to '/opt')" + echo "To deinstall, use the command:" + echo "sh deinstall.sh DIR" + exit 1 +fi diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf new file mode 100644 index 000000000..002bc0592 --- /dev/null +++ b/tools/niminst/makefile.nimf @@ -0,0 +1,219 @@ +#? stdtmpl(subsChar='?') | standard +#proc generateMakefile(c: ConfigData): string = +# result = "# Generated from niminst\n" & +# "# Template is in tools/niminst/makefile.nimf\n" & +# "# To regenerate run ``niminst csource`` or ``koch csource``\n" + +CC ??= gcc +CFLAGS += -Ic_code ?{c.ccompiler.flags} +LDFLAGS += ?{c.linker.flags} +binDir = ?{firstBinPath(c).toUnix} + +koch := $(shell sh -c 'test -s ../koch.nim && echo "yes"') +ifeq ($(koch),yes) + binDir = ../bin +endif + +target := ?{"$(binDir)/" & toLowerAscii(c.name)} + + +ucpu := $(shell sh -c 'uname -m | tr "[:upper:]" "[:lower:]"') +ifeq ($(OS),Windows_NT) + uos := windows +else + uos := $(shell sh -c 'uname | tr "[:upper:]" "[:lower:]"') +endif + +ifeq ($(uos),linux) + myos = linux + ## add -lrt to avoid "undefined reference to `clock_gettime'" with glibc<2.17 + LDFLAGS += -ldl -lm -lrt +endif +ifeq ($(uos),dragonfly) + myos = freebsd + LDFLAGS += -lm +endif +ifeq ($(uos),freebsd) + myos= freebsd + CC = clang + LDFLAGS += -lm +endif +ifeq ($(uos),openbsd) + myos = openbsd + LDFLAGS += -lm +endif +ifeq ($(uos),netbsd) + myos = netbsd + LDFLAGS += -lm + ucpu = $(shell sh -c 'uname -p') +endif +ifeq ($(uos),darwin) + myos = macosx + CC = clang + LDFLAGS += -ldl -lm + ifeq ($(HOSTTYPE),x86_64) + ucpu = amd64 + endif +endif +ifeq ($(uos),aix) + myos = aix + LDFLAGS += -dl -lm +endif +ifeq ($(uos),solaris) + myos = solaris + LDFLAGS += -ldl -lm -lsocket -lnsl +endif +ifeq ($(uos),sun) + myos = solaris + LDFLAGS += -ldl -lm -lsocket -lnsl +endif +ifeq ($(uos),haiku) + myos = haiku +endif +ifeq ($(uos),windows) + myos = windows + target := $(target).exe +endif +ifndef myos + $(error unknown operating system: $(uos)) +endif + +ifeq ($(ucpu),i386) + mycpu = i386 +endif +ifeq ($(ucpu),i486) + mycpu = i386 +endif +ifeq ($(ucpu),i586) + mycpu = i386 +endif +ifeq ($(ucpu),i686) + mycpu = i386 +endif +ifeq ($(ucpu),bepc) + mycpu = i386 +endif +ifeq ($(ucpu),i86pc) + mycpu = i386 +endif +ifeq ($(ucpu),amd64) + mycpu = amd64 +endif +ifeq ($(ucpu),x86-64) + mycpu = amd64 +endif +ifeq ($(ucpu),x86_64) + mycpu = amd64 +endif +ifeq ($(ucpu),parisc64) + mycpu = hppa +endif +ifeq ($(ucpu),s390x) + mycpu = s390x +endif +ifeq ($(ucpu),sparc) + mycpu = sparc +endif +ifeq ($(ucpu),sparc64) + mycpu = sparc64 +endif +ifeq ($(ucpu),sun) + mycpu = sparc +endif +ifeq ($(ucpu),ppc64le) + mycpu = powerpc64el +endif +ifeq ($(ucpu),ppc64) + mycpu = powerpc64 + ifeq ($(myos),linux) + CFLAGS += -m64 + LDFLAGS += -m64 + endif +endif +ifeq ($(ucpu),powerpc) + mycpu = powerpc + ifeq ($(myos),freebsd) + mycpu = $(shell sh -c 'uname -p | tr "[:upper:]" "[:lower:]"') + CFLAGS += -m64 + LDFLAGS += -m64 + ifeq ($(mycpu),powerpc64le) + mycpu = powerpc64el + endif + endif +endif +ifeq ($(ucpu),ppc) + mycpu = powerpc +endif +ifneq (,$(filter $(ucpu), mips mips64)) + mycpu = $(shell /bin/sh -c '"$(CC)" -dumpmachine | sed "s/-.*//"') + ifeq (,$(filter $(mycpu), mips mipsel mips64 mips64el)) + $(error unknown MIPS target: $(mycpu)) + endif +endif +ifeq ($(ucpu),arm) + mycpu = arm +endif +ifeq ($(ucpu),armeb) + mycpu = arm +endif +ifeq ($(ucpu),armel) + mycpu = arm +endif +ifeq ($(ucpu),armv6l) + mycpu = arm +endif +ifeq ($(ucpu),armv7l) + mycpu = arm +endif +ifeq ($(ucpu),armv7hl) + mycpu = arm +endif +ifeq ($(ucpu),armv8l) + mycpu = arm +endif +ifeq ($(ucpu),aarch64) + mycpu = arm64 +endif +ifeq ($(ucpu),arm64) + mycpu = arm64 +endif +ifeq ($(ucpu),riscv64) + mycpu = riscv64 +endif +ifeq ($(ucpu),e2k) + mycpu = e2k +endif +ifeq ($(ucpu),loongarch64) + mycpu = loongarch64 +endif +ifndef mycpu + $(error unknown CPU architecture: $(ucpu) See makefile.nimf) +endif + +# for osA in 1..c.oses.len: +ifeq ($(myos),?{c.oses[osA-1]}) +# for cpuA in 1..c.cpus.len: + ifeq ($(mycpu),?{c.cpus[cpuA-1]}) +# var oFiles = "" +# for ff in c.cfiles[osA][cpuA].items: +# oFiles.add(" " & changeFileExt(ff.toUnix, "o")) +# end for + oFiles =?oFiles + endif +# end for +endif +# end for + +ifeq ($(strip $(oFiles)),) + $(error no C code generated for: [$(myos): $(mycpu)]) +endif + +$(target): $(oFiles) + @mkdir -p $(binDir) + $(CC) -o $@ $^ $(LDFLAGS) + @echo "SUCCESS" + +.PHONY: clean + +clean: + rm -f $(oFiles) ?{"$(binDir)/" & toLowerAscii(c.name)} diff --git a/tools/niminst/nim-file.ico b/tools/niminst/nim-file.ico new file mode 100644 index 000000000..0685fedcb --- /dev/null +++ b/tools/niminst/nim-file.ico Binary files differdiff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim new file mode 100644 index 000000000..40ee79814 --- /dev/null +++ b/tools/niminst/niminst.nim @@ -0,0 +1,751 @@ +# +# +# The Nim Installation Generator +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import + os, strutils, parseopt, parsecfg, strtabs, streams, debcreation + +import ../../dist/checksums/src/checksums/sha1 + +when defined(nimPreviewSlimSystem): + import std/syncio + +when not defined(nimHasEffectsOf): + {.pragma: effectsOf.} + +const + maxOS = 20 # max number of OSes + maxCPU = 20 # max number of CPUs + buildShFile = "build.sh" + buildBatFile = "build.bat" + buildBatFile32 = "build32.bat" + buildBatFile64 = "build64.bat" + makeFile = "makefile" + installShFile = "install.sh" + deinstallShFile = "deinstall.sh" + +type + AppType = enum appConsole, appGUI + Action = enum + actionNone, # action not yet known + actionCSource # action: create C sources + actionInno, # action: create Inno Setup installer + actionNsis, # action: create NSIS installer + actionScripts # action: create install and deinstall scripts + actionZip # action: create zip file + actionXz, # action: create xz file + actionDeb # action: prepare deb package + + FileCategory = enum + fcWinBin, # binaries for Windows + fcConfig, # configuration files + fcData, # data files + fcDoc, # documentation files + fcLib, # library files + fcOther, # other files; will not be copied on UNIX + fcWindows, # files only for Windows + fcUnix, # files only for Unix; must be after ``fcWindows`` + fcUnixBin, # binaries for Unix + fcDocStart, # links to documentation for Windows installer + fcNimble # nimble package files to copy to /opt/nimble/pkgs/pkg-ver + + ConfigData = object of RootObj + actions: set[Action] + cat: array[FileCategory, seq[string]] + binPaths, authors, oses, cpus, downloads: seq[string] + cfiles: array[1..maxOS, array[1..maxCPU, seq[string]]] + platforms: array[1..maxOS, array[1..maxCPU, bool]] + ccompiler, linker, innosetup, nsisSetup: tuple[path, flags: string] + name, displayName, version, description, license, infile, outdir: string + mainfile, libpath: string + innoSetupFlag, installScript, uninstallScript: bool + explicitPlatforms: bool + vars: StringTableRef + app: AppType + nimArgs: string + debOpts: TDebOptions + nimblePkgName: string + +const + unixDirVars: array[fcConfig..fcLib, string] = [ + "$configdir", "$datadir", "$docdir", "$libdir" + ] + +func iniConfigData(c: var ConfigData) = + c.actions = {} + for i in low(FileCategory)..high(FileCategory): c.cat[i] = @[] + c.binPaths = @[] + c.authors = @[] + c.oses = @[] + c.cpus = @[] + c.downloads = @[] + c.ccompiler = ("", "") + c.linker = ("", "") + c.innosetup = ("", "") + c.nsisSetup = ("", "") + c.name = "" + c.displayName = "" + c.version = "" + c.description = "" + c.license = "" + c.infile = "" + c.mainfile = "" + c.outdir = "" + c.nimArgs = "" + c.libpath = "" + c.innoSetupFlag = false + c.installScript = false + c.uninstallScript = false + c.vars = newStringTable(modeStyleInsensitive) + + c.debOpts.buildDepends = "" + c.debOpts.pkgDepends = "" + c.debOpts.shortDesc = "" + c.debOpts.licenses = @[] + +func firstBinPath(c: ConfigData): string = + if c.binPaths.len > 0: result = c.binPaths[0] + else: result = "" + +func `\`(a, b: string): string = + result = if a.len == 0: b else: a & '\\' & b + +template toUnix(s: string): string = s.replace('\\', '/') +template toWin(s: string): string = s.replace('/', '\\') + +func skipRoot(f: string): string = + # "abc/def/xyz" --> "def/xyz" + var i = 0 + result = "" + for component in split(f, {DirSep, AltSep}): + if i > 0: result = result / component + inc i + if result.len == 0: result = f + +include "inno.nimf" +include "nsis.nimf" +include "buildsh.nimf" +include "makefile.nimf" +include "buildbat.nimf" +include "install.nimf" +include "deinstall.nimf" + +# ------------------------- configuration file ------------------------------- + +const + Version = "1.0" + Usage = "niminst - Nim Installation Generator Version " & Version & """ + + (c) 2015 Andreas Rumpf +Usage: + niminst [options] command[;command2...] ini-file[.ini] [compile_options] +Command: + csource build C source code for source based installations + scripts build install and deinstall scripts + zip build the ZIP file + inno build the Inno Setup installer + nsis build the NSIS Setup installer + deb create files for debhelper +Options: + -o, --output:dir set the output directory + -m, --main:file set the main nim file, by default ini-file with .nim + extension + --var:name=value set the value of a variable + -h, --help shows this help + -v, --version shows the version +Compile_options: + will be passed to the Nim compiler +""" + +proc parseCmdLine(c: var ConfigData) = + var p = initOptParser() + while true: + next(p) + var kind = p.kind + var key = p.key + var val = p.val + case kind + of cmdArgument: + if c.actions == {}: + for a in split(normalize(key), {';', ','}): + case a + of "csource": incl(c.actions, actionCSource) + of "scripts": incl(c.actions, actionScripts) + of "zip": incl(c.actions, actionZip) + of "xz": incl(c.actions, actionXz) + of "inno": incl(c.actions, actionInno) + of "nsis": incl(c.actions, actionNsis) + of "deb": incl(c.actions, actionDeb) + else: quit(Usage) + else: + c.infile = addFileExt(key, "ini") + c.nimArgs = cmdLineRest(p) + break + of cmdLongOption, cmdShortOption: + case normalize(key) + of "help", "h": + stdout.write(Usage) + quit(0) + of "version", "v": + stdout.write(Version & "\n") + quit(0) + of "o", "output": c.outdir = val + of "m", "main": c.mainfile = changeFileExt(val, "nim") + of "var": + var idx = val.find('=') + if idx < 0: quit("invalid command line") + c.vars[substr(val, 0, idx-1)] = substr(val, idx+1) + else: quit(Usage) + of cmdEnd: break + if c.infile.len == 0: quit(Usage) + if c.mainfile.len == 0: c.mainfile = changeFileExt(c.infile, "nim") + +proc eqT(a, b: string; t: proc (a: char): char {.nimcall.}): bool {.effectsOf: t.} = + ## equality under a transformation ``t``. candidate for the stdlib? + var i = 0 + var j = 0 + while i < a.len and j < b.len: + let aa = t a[i] + let bb = t b[j] + if aa == '\0': + inc i + if bb == '\0': inc j + elif bb == '\0': inc j + else: + if aa != bb: return false + inc i + inc j + result = i >= a.len and j >= b.len + +func tPath(c: char): char = + if c == '\\': '/' + else: c + +func ignoreFile(f, explicit: string, allowHtml: bool): bool = + let (_, name, ext) = splitFile(f) + let html = if not allowHtml: ".html" else: "" + result = (ext in ["", ".exe", ".idx", ".o", ".obj", ".dylib"] or + ext == html or name[0] == '.') and not eqT(f, explicit, tPath) + +proc walkDirRecursively(s: var seq[string], root, explicit: string, + allowHtml: bool) = + let tail = splitPath(root).tail + if tail == "nimcache" or tail[0] == '.': + return + let allowHtml = allowHtml or tail == "doc" + for k, f in walkDir(root): + if f[0] == '.' and root[0] != '.': + discard "skip .git directories etc" + else: + case k + of pcFile, pcLinkToFile: + if not ignoreFile(f, explicit, allowHtml): + add(s, unixToNativePath(f)) + of pcDir: + walkDirRecursively(s, f, explicit, allowHtml) + of pcLinkToDir: discard + +proc addFiles(s: var seq[string], patterns: seq[string]) = + for p in items(patterns): + if dirExists(p): + walkDirRecursively(s, p, p, false) + else: + var i = 0 + for f in walkPattern(p): + if dirExists(f): + walkDirRecursively(s, f, p, false) + elif not ignoreFile(f, p, false): + add(s, unixToNativePath(f)) + inc(i) + if i == 0: echo("[Warning] No file found that matches: " & p) + +proc pathFlags(p: var CfgParser, k, v: string, + t: var tuple[path, flags: string]) = + case normalize(k) + of "path": t.path = v + of "flags": t.flags = v + else: quit(errorStr(p, "unknown variable: " & k)) + +proc filesOnly(p: var CfgParser, k, v: string, dest: var seq[string]) = + case normalize(k) + of "files": addFiles(dest, split(v, {';'})) + else: quit(errorStr(p, "unknown variable: " & k)) + +proc yesno(p: var CfgParser, v: string): bool = + case normalize(v) + of "yes", "y", "on", "true": + result = true + of "no", "n", "off", "false": + result = false + else: quit(errorStr(p, "unknown value; use: yes|no")) + +func incl(s: var seq[string], x: string): int = + for i in 0 ..< s.len: + if cmpIgnoreStyle(s[i], x) == 0: return i + s.add(x) + result = s.len-1 + +func platforms(c: var ConfigData, v: string) = + for line in splitLines(v): + let p = line.find(": ") + if p <= 1: continue + let os = line.substr(0, p-1).strip + let cpus = line.substr(p+1).strip + c.oses.add(os) + for cpu in cpus.split(';'): + let cpuIdx = c.cpus.incl(cpu) + c.platforms[c.oses.len][cpuIdx+1] = true + +proc parseIniFile(c: var ConfigData) = + var + p: CfgParser + section = "" + hasCpuOs = false + var input = newFileStream(c.infile, fmRead) + if input != nil: + open(p, input, c.infile) + while true: + var k = next(p) + case k.kind + of cfgEof: break + of cfgSectionStart: + section = normalize(k.section) + of cfgKeyValuePair: + var v = `%`(k.value, c.vars, {useEnvironment, useEmpty}) + c.vars[k.key] = v + + case section + of "project": + case normalize(k.key) + of "name": c.name = v + of "displayname": c.displayName = v + of "version": c.version = v + of "os": + c.oses = split(v, {';'}) + hasCpuOs = true + if c.explicitPlatforms: + quit(errorStr(p, "you cannot have both 'platforms' and 'os'")) + of "cpu": + c.cpus = split(v, {';'}) + hasCpuOs = true + if c.explicitPlatforms: + quit(errorStr(p, "you cannot have both 'platforms' and 'cpu'")) + of "platforms": + platforms(c, v) + c.explicitPlatforms = true + if hasCpuOs: + quit(errorStr(p, "you cannot have both 'platforms' and 'os'")) + of "authors": c.authors = split(v, {';'}) + of "description": c.description = v + of "app": + case normalize(v) + of "console": c.app = appConsole + of "gui": c.app = appGUI + else: quit(errorStr(p, "expected: console or gui")) + of "license": c.license = unixToNativePath(k.value) + else: quit(errorStr(p, "unknown variable: " & k.key)) + of "var": discard + of "winbin": filesOnly(p, k.key, v, c.cat[fcWinBin]) + of "config": filesOnly(p, k.key, v, c.cat[fcConfig]) + of "data": filesOnly(p, k.key, v, c.cat[fcData]) + of "documentation": + case normalize(k.key) + of "files": addFiles(c.cat[fcDoc], split(v, {';'})) + of "start": addFiles(c.cat[fcDocStart], split(v, {';'})) + else: quit(errorStr(p, "unknown variable: " & k.key)) + of "lib": filesOnly(p, k.key, v, c.cat[fcLib]) + of "other": filesOnly(p, k.key, v, c.cat[fcOther]) + of "windows": + case normalize(k.key) + of "files": addFiles(c.cat[fcWindows], split(v, {';'})) + of "binpath": c.binPaths = split(v, {';'}) + of "innosetup": c.innoSetupFlag = yesno(p, v) + of "download": c.downloads.add(v) + else: quit(errorStr(p, "unknown variable: " & k.key)) + of "unix": + case normalize(k.key) + of "files": addFiles(c.cat[fcUnix], split(v, {';'})) + of "installscript": c.installScript = yesno(p, v) + of "uninstallscript": c.uninstallScript = yesno(p, v) + else: quit(errorStr(p, "unknown variable: " & k.key)) + of "unixbin": filesOnly(p, k.key, v, c.cat[fcUnixBin]) + of "innosetup": pathFlags(p, k.key, v, c.innosetup) + of "nsis": pathFlags(p, k.key, v, c.nsisSetup) + of "ccompiler": pathFlags(p, k.key, v, c.ccompiler) + of "linker": pathFlags(p, k.key, v, c.linker) + of "deb": + case normalize(k.key) + of "builddepends": + c.debOpts.buildDepends = v + of "packagedepends", "pkgdepends": + c.debOpts.pkgDepends = v + of "shortdesc": + c.debOpts.shortDesc = v + of "licenses": + # file,license;file,license; + var i = 0 + var file = "" + var license = "" + var afterComma = false + while i < v.len(): + case v[i] + of ',': + afterComma = true + of ';': + if file == "" or license == "": + quit(errorStr(p, "Invalid `licenses` key.")) + c.debOpts.licenses.add((file, license)) + afterComma = false + file = "" + license = "" + else: + if afterComma: license.add(v[i]) + else: file.add(v[i]) + inc(i) + else: quit(errorStr(p, "unknown variable: " & k.key)) + of "nimble": + case normalize(k.key) + of "pkgname": + c.nimblePkgName = v + of "pkgfiles": + addFiles(c.cat[fcNimble], split(v, {';'})) + else: + quit(errorStr(p, "invalid key: " & k.key)) + else: quit(errorStr(p, "invalid section: " & section)) + + of cfgOption: quit(errorStr(p, "syntax error")) + of cfgError: quit(errorStr(p, k.msg)) + close(p) + if c.name.len == 0: c.name = changeFileExt(extractFilename(c.mainfile), "") + if c.displayName.len == 0: c.displayName = c.name + else: + quit("cannot open: " & c.infile) + +# ------------------------- generate source based installation --------------- + +proc readCFiles(c: var ConfigData, osA, cpuA: int) = + var p: CfgParser + var f = splitFile(c.infile).dir / "mapping.txt" + c.cfiles[osA][cpuA] = @[] + var input = newFileStream(f, fmRead) + var section = "" + if input != nil: + open(p, input, f) + while true: + var k = next(p) + case k.kind + of cfgEof: break + of cfgSectionStart: + section = normalize(k.section) + of cfgKeyValuePair: + case section + of "ccompiler": pathFlags(p, k.key, k.value, c.ccompiler) + of "linker": + pathFlags(p, k.key, k.value, c.linker) + # HACK: we conditionally add ``-lm -ldl``, so remove them from the + # linker flags: + c.linker.flags = c.linker.flags.replaceWord("-lm").replaceWord( + "-ldl").replaceWord("-lroot").replaceWord( + "-lnetwork").strip + else: + if cmpIgnoreStyle(k.key, "libpath") == 0: + c.libpath = k.value + of cfgOption: + if section == "cfiles" and cmpIgnoreStyle(k.key, "file") == 0: + add(c.cfiles[osA][cpuA], k.value) + of cfgError: quit(errorStr(p, k.msg)) + close(p) + else: + quit("Cannot open: " & f) + +func buildDir(os, cpu: int): string = + "c_code" / ($os & "_" & $cpu) + +func getOutputDir(c: var ConfigData): string = + if c.outdir.len > 0: c.outdir else: "build" + +proc writeFile(filename, content, newline: string) = + var f: File + if open(f, filename, fmWrite): + for x in splitLines(content): + write(f, x) + write(f, newline) + close(f) + else: + quit("Cannot open for writing: " & filename) + +proc deduplicateFiles(c: var ConfigData) = + var tab = newStringTable() + let build = getOutputDir(c) + for osA in countup(1, c.oses.len): + for cpuA in countup(1, c.cpus.len): + if c.explicitPlatforms and not c.platforms[osA][cpuA]: continue + for dup in mitems(c.cfiles[osA][cpuA]): + let key = $secureHashFile(build / dup) + let val = buildDir(osA, cpuA) / extractFilename(dup) + let orig = tab.getOrDefault(key) + if orig.len > 0: + # file is identical, so delete duplicate: + removeFile(dup) + dup = orig + else: + tab[key] = val + +proc writeInstallScripts(c: var ConfigData) = + if c.installScript: + writeFile(installShFile, generateInstallScript(c), "\10") + inclFilePermissions(installShFile, {fpUserExec, fpGroupExec, fpOthersExec}) + if c.uninstallScript: + writeFile(deinstallShFile, generateDeinstallScript(c), "\10") + inclFilePermissions(deinstallShFile, {fpUserExec, fpGroupExec, fpOthersExec}) + +template gatherFiles(fun, libpath, outDir) = + block: + template copySrc(src) = + let dst = outDir / extractFilename(src) + when false: echo (dst, dst) + fun(src, dst) + + for f in walkFiles(libpath / "lib/*.h"): copySrc(f) + # commenting out for now, see discussion in https://github.com/nim-lang/Nim/pull/13413 + # copySrc(libpath / "lib/wrappers/linenoise/linenoise.h") + +proc exe(f: string): string = + result = addFileExt(f, ExeExt) + when defined(windows): + result = result.replace('/','\\') + +proc findNim(): string = + let nim = "nim".exe + result = quoteShell("bin" / nim) + if not fileExists(result): + result = "nim" + +proc srcdist(c: var ConfigData) = + let cCodeDir = getOutputDir(c) / "c_code" + if not dirExists(cCodeDir): createDir(cCodeDir) + gatherFiles(copyFile, c.libpath, cCodeDir) + var winIndex = -1 + var intel32Index = -1 + var intel64Index = -1 + for osA in 1..c.oses.len: + let osname = c.oses[osA-1] + if osname.cmpIgnoreStyle("windows") == 0: winIndex = osA + for cpuA in 1..c.cpus.len: + if c.explicitPlatforms and not c.platforms[osA][cpuA]: continue + let cpuname = c.cpus[cpuA-1] + if cpuname.cmpIgnoreStyle("i386") == 0: intel32Index = cpuA + elif cpuname.cmpIgnoreStyle("amd64") == 0: intel64Index = cpuA + var dir = getOutputDir(c) / buildDir(osA, cpuA) + if dirExists(dir): removeDir(dir) + createDir(dir) + var cmd = ("$# compile -f --incremental:off --compileonly " & + "--gen_mapping --cc:gcc --skipUserCfg" & + " --os:$# --cpu:$# $# $#") % + [findNim(), osname, cpuname, c.nimArgs, c.mainfile] + echo(cmd) + if execShellCmd(cmd) != 0: + quit("Error: call to nim compiler failed") + readCFiles(c, osA, cpuA) + for i in 0 .. c.cfiles[osA][cpuA].len-1: + let dest = dir / extractFilename(c.cfiles[osA][cpuA][i]) + let relDest = buildDir(osA, cpuA) / extractFilename(c.cfiles[osA][cpuA][i]) + copyFile(dest=dest, source=c.cfiles[osA][cpuA][i]) + c.cfiles[osA][cpuA][i] = relDest + # second pass: remove duplicate files + deduplicateFiles(c) + writeFile(getOutputDir(c) / buildShFile, generateBuildShellScript(c), "\10") + inclFilePermissions(getOutputDir(c) / buildShFile, {fpUserExec, fpGroupExec, fpOthersExec}) + writeFile(getOutputDir(c) / makeFile, generateMakefile(c), "\10") + if winIndex >= 0: + if intel32Index >= 0 or intel64Index >= 0: + writeFile(getOutputDir(c) / buildBatFile, + generateBuildBatchScript(c, winIndex, intel32Index, intel64Index), "\13\10") + if intel32Index >= 0: + writeFile(getOutputDir(c) / buildBatFile32, "SET ARCH=32\nCALL build.bat\n") + if intel64Index >= 0: + writeFile(getOutputDir(c) / buildBatFile64, "SET ARCH=64\nCALL build.bat\n") + writeInstallScripts(c) + +# --------------------- generate inno setup ----------------------------------- +proc setupDist(c: var ConfigData) = + let scrpt = generateInnoSetup(c) + let n = "build" / "install_$#_$#.iss" % [toLowerAscii(c.name), c.version] + writeFile(n, scrpt, "\13\10") + when defined(windows): + if c.innosetup.path.len == 0: + c.innosetup.path = "iscc.exe" + let outcmd = if c.outdir.len == 0: "build" else: c.outdir + let cmd = "$# $# /O$# $#" % [quoteShell(c.innosetup.path), + c.innosetup.flags, outcmd, n] + echo(cmd) + if execShellCmd(cmd) == 0: + removeFile(n) + else: + quit("External program failed") + +# --------------------- generate NSIS setup ----------------------------------- +proc setupDist2(c: var ConfigData) = + let scrpt = generateNsisSetup(c) + let n = "build" / "install_$#_$#.nsi" % [toLowerAscii(c.name), c.version] + writeFile(n, scrpt, "\13\10") + when defined(windows): + if c.nsisSetup.path.len == 0: + c.nsisSetup.path = "makensis.exe" + let outcmd = if c.outdir.len == 0: "build" else: c.outdir + let cmd = "$# $# /O$# $#" % [quoteShell(c.nsisSetup.path), + c.nsisSetup.flags, outcmd, n] + echo(cmd) + if execShellCmd(cmd) == 0: + removeFile(n) + else: + quit("External program failed") + +proc xzDist(c: var ConfigData; windowsZip=false) = + let proj = toLowerAscii(c.name) & "-" & c.version + let tmpDir = if c.outdir.len == 0: "build" else: c.outdir + + proc processFile(destFile, src: string) = + let dest = tmpDir / destFile + when false: echo "Copying ", src, " to ", dest + if not fileExists(src): + echo "[Warning] Source file doesn't exist: ", src + let destDir = dest.splitFile.dir + if not dirExists(destDir): createDir(destDir) + copyFileWithPermissions(src, dest) + + if not windowsZip and not fileExists("build" / buildBatFile): + quit("No C sources found in ./build/, please build by running " & + "./koch csource -d:danger.") + + if not windowsZip: + processFile(proj / buildBatFile, "build" / buildBatFile) + processFile(proj / buildBatFile32, "build" / buildBatFile32) + processFile(proj / buildBatFile64, "build" / buildBatFile64) + processFile(proj / buildShFile, "build" / buildShFile) + processFile(proj / makeFile, "build" / makeFile) + processFile(proj / installShFile, installShFile) + processFile(proj / deinstallShFile, deinstallShFile) + template processFileAux(src, dst) = processFile(dst, src) + gatherFiles(processFileAux, c.libpath, proj / "c_code") + for osA in 1..c.oses.len: + for cpuA in 1..c.cpus.len: + var dir = buildDir(osA, cpuA) + for k, f in walkDir("build" / dir): + if k == pcFile: processFile(proj / dir / extractFilename(f), f) + else: + for f in items(c.cat[fcWinBin]): + let filename = f.extractFilename + processFile(proj / "bin" / filename, f) + + let osSpecific = if windowsZip: fcWindows else: fcUnix + for cat in items({fcConfig..fcOther, osSpecific, fcNimble}): + echo("Current category: ", cat) + for f in items(c.cat[cat]): processFile(proj / f, f) + + # Copy the .nimble file over + let nimbleFile = c.nimblePkgName & ".nimble" + processFile(proj / nimbleFile, nimbleFile) + + when true: + let oldDir = getCurrentDir() + setCurrentDir(tmpDir) + try: + if windowsZip: + if execShellCmd("7z a -tzip $1.zip $1" % proj) != 0: + echo("External program failed (zip)") + when false: + writeFile("config.txt", """;!@Install@!UTF-8! +Title="Nim v$1" +BeginPrompt="Do you want to configure Nim v$1?" +RunProgram="tools\downloader.exe" +;!@InstallEnd@!""" % NimVersion) + if execShellCmd("7z a -sfx7zS2.sfx -t7z $1.exe $1" % proj) != 0: + echo("External program failed (7z)") + else: + if execShellCmd("gtar cf $1.tar --exclude=.DS_Store $1" % + proj) != 0: + # try old 'tar' without --exclude feature: + if execShellCmd("tar cf $1.tar $1" % proj) != 0: + echo("External program failed") + + if execShellCmd("xz -9f $1.tar" % proj) != 0: + echo("External program failed") + finally: + setCurrentDir(oldDir) + +# -- prepare build files for .deb creation + +proc debDist(c: var ConfigData) = + if not fileExists(getOutputDir(c) / "build.sh"): quit("No build.sh found.") + if not fileExists(getOutputDir(c) / "install.sh"): quit("No install.sh found.") + + if c.debOpts.shortDesc == "": quit("shortDesc must be set in the .ini file.") + if c.debOpts.licenses.len == 0: + echo("[Warning] No licenses specified for .deb creation.") + + # -- Copy files into /tmp/.. + echo("Copying source to tmp/niminst/deb/") + var currentSource = getCurrentDir() + var workingDir = getTempDir() / "niminst" / "deb" + var upstreamSource = (c.name.toLowerAscii() & "-" & c.version) + + createDir(workingDir / upstreamSource) + + template copyNimDist(f, dest: string) = + createDir((workingDir / upstreamSource / dest).splitFile.dir) + copyFile(currentSource / f, workingDir / upstreamSource / dest) + + # Don't copy all files, only the ones specified in the config: + copyNimDist(buildShFile, buildShFile) + copyNimDist(makeFile, makeFile) + copyNimDist(installShFile, installShFile) + createDir(workingDir / upstreamSource / "build") + gatherFiles(copyNimDist, c.libpath, "build") + for osA in 1..c.oses.len: + for cpuA in 1..c.cpus.len: + var dir = buildDir(osA, cpuA) + for k, f in walkDir(dir): + if k == pcFile: copyNimDist(f, dir / extractFilename(f)) + for cat in items({fcConfig..fcOther, fcUnix}): + for f in items(c.cat[cat]): copyNimDist(f, f) + + # -- Create necessary build files for debhelper. + + let mtnName = c.vars["mtnname"] + let mtnEmail = c.vars["mtnemail"] + + prepDeb(c.name, c.version, mtnName, mtnEmail, c.debOpts.shortDesc, + c.description, c.debOpts.licenses, c.cat[fcUnixBin], c.cat[fcConfig], + c.cat[fcDoc], c.cat[fcLib], c.debOpts.buildDepends, + c.debOpts.pkgDepends) + +# ------------------- main ---------------------------------------------------- + +proc main() = + var c: ConfigData + iniConfigData(c) + parseCmdLine(c) + parseIniFile(c) + if actionInno in c.actions: + setupDist(c) + if actionNsis in c.actions: + setupDist2(c) + if actionCSource in c.actions: + srcdist(c) + if actionScripts in c.actions: + writeInstallScripts(c) + if actionZip in c.actions: + xzDist(c, true) + if actionXz in c.actions: + xzDist(c) + if actionDeb in c.actions: + debDist(c) + +when isMainModule: + main() diff --git a/tools/niminst/nsis.nimf b/tools/niminst/nsis.nimf new file mode 100644 index 000000000..f4eb1d0cd --- /dev/null +++ b/tools/niminst/nsis.nimf @@ -0,0 +1,445 @@ +#? stdtmpl(subsChar='?') | standard +#proc generateNsisSetup(c: ConfigData): string = +# result = "; NSIS script generated by niminst\n" & +# "; To regenerate run ``niminst nsis`` or ``koch nsis``\n" + +;-------------------------------- +; Included headers + ; Modern User Interface 2.0 Header + !include "MUI2.nsh" + + ; File Functions Header, used to get the current drive root. + !include "FileFunc.nsh" + +;-------------------------------- +; Global variables and defines + !define PRODUCT_NAME "?c.displayName" + !define PRODUCT_VERSION "?c.version" + !define PRODUCT_PUBLISHER "?c.authors" + !define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\?{c.name}.exe" + !define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" + !define PRODUCT_UNINST_ROOT_KEY "HKCU" + !define PRODUCT_STARTMENU_REGVAL "NSIS:StartMenuDir" + + +;-------------------------------- +; General Setup Information + + ; Name and output file + Name "?{c.name} ?{c.version}" + OutFile "?{c.name}_?{c.version}.exe" + + ; Default installation folder + ; This is changed later (in .onInit) to the root directory, if possible. + InstallDir "$PROGRAMFILES?{when sizeof(int) == 8: "64" else: ""}\?{c.name}-?{c.version}\" + + ; Get installation folder from registry if available + InstallDirRegKey HKCU "Software\c.name\c.version" "" + + ; Request user level application privileges. + RequestExecutionLevel user + + ; Allow installation to the root drive directory. + AllowRootDirInstall false + + ; Maximum compression! + SetCompressor /SOLID /FINAL lzma + + ; Installer and Uninstaller Icons + ; Icon "nim.ico" + ; UninstallIcon "nim.ico" + + ; Set installation details to be shown by default + ShowInstDetails show + ShowUnInstDetails show + +;-------------------------------- +; Interface Settings + + ; Warn the user if aborting during installation/uninstallation + !define MUI_ABORTWARNING + !define MUI_UNABORTWARNING + + ; Don't show a description for sections + !define MUI_COMPONENTSPAGE_NODESC + +;-------------------------------- +; Pages + + ; Setup the installer pages + !insertmacro MUI_PAGE_WELCOME + !insertmacro MUI_PAGE_LICENSE "?{expandFilename(c.license)}" + !insertmacro MUI_PAGE_COMPONENTS + !insertmacro MUI_PAGE_DIRECTORY + + ; Setup the start menu entry page + var ICONS_GROUP + !define MUI_STARTMENUPAGE_DEFAULTFOLDER "?{c.displayName}" + !define MUI_STARTMENUPAGE_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}" + !define MUI_STARTMENUPAGE_REGISTRY_KEY "${PRODUCT_UNINST_KEY}" + !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${PRODUCT_STARTMENU_REGVAL}" + !insertmacro MUI_PAGE_STARTMENU Application $ICONS_GROUP + + !insertmacro MUI_PAGE_INSTFILES + !insertmacro MUI_PAGE_FINISH + + ; Setup the uninstaller pages + !insertmacro MUI_UNPAGE_CONFIRM + !insertmacro MUI_UNPAGE_INSTFILES + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" + +;-------------------------------- +;Installer Sections + + ; The core section. This is comprised of a base Nim installation, + ; such as what would be retrieved via git, and an already bootstrapped + ; Nim binary. + Section "Core Files" CoreSection + ; This is a mandotory section + SectionIn RO + + ; Output files to the base installation directory + SetOutPath "$INSTDIR" + + ; Only overwrite newer files + SetOverwrite ifnewer + + ; Write all the files to the output directory. + #for i in low(FileCategory)..fcWindows: + # for f in items(c.cat[i]): + SetOutPath "$INSTDIR\?{splitFile(f).dir.toWin}" + File "?{expandFilename(f).toWin}" + # end for + #end for + + ; Write out the uninstaller + WriteUninstaller "$INSTDIR\uninstaller.exe" + SectionEnd + + Section "-Add Registry Keys" RegistrySection + ; Write application registry keys + WriteRegStr HKCU "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\bin\?{c.name}.exe" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninstaller.exe" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\bin\?{c.name}.exe" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" + ; Reset the output path + SetOutPath "$INSTDIR" + SectionEnd + + ; Section for adding the shortcuts related to files and applications + Section "-Setup Shortcuts" ShortcutsSection + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + CreateDirectory "$SMPROGRAMS\$ICONS_GROUP" + + #if c.app == appConsole: + CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\?{c.displayName}.lnk" "$INSTDIR\start.bat" + CreateShortCut "$DESKTOP\?{c.displayName}.lnk" "$INSTDIR\start.bat" + #else: + CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\?{c.displayName}.lnk" "$INSTDIR\?{c.name}.exe" + CreateShortCut "$DESKTOP\?{c.displayName}.lnk" "$INSTDIR\?{c.name}.exe" + #end if + + ; Write the shortcut to the uninstaller + CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk" "$INSTDIR\uninstaller.exe" + !insertmacro MUI_STARTMENU_WRITE_END + SectionEnd + + ; Section for adding tools to the PATH variable + Section "Setup Path Environment" PathSection + Push "$INSTDIR\bin" + Call AddToPath + Push "$INSTDIR\dist\mingw" + Call AddToPath + Push "$INSTDIR\dist\mingw\bin" + Call AddToPath + Push "$PROFILE\.nimble\bin" + Call AddToPath + SectionEnd + + ; The downloadable sections. These sections are automatically generated by + ; niminst and the template filters. + #var i = 0 + #for download in c.downloads: + # inc i + # let d = download.split('|') + # if d.len != 5 and d.len != 6: + # quit("download string needs 5..6 parts: " & download) + # end if + # let sectionName = d[0] + # let dir = d[1] + # let zipName = d[2] + # let size = d[3] + # let url = d[4] + Section /o "?sectionName" ?{i}Section + ; Add the section size to the total size. + AddSize ?size + + ; Download the file, and if successful, extract it to the given directory + ; otherwise, + retry: + NSISdl::download "?url" "$TEMP\?zipName" + Pop $0 + ${If} $0 == "success" + ZipDLL::extractall "$TEMP\?zipName" "$INSTDIR\?dir" + Delete "$TEMP\?zipName" + ${ElseIf} $0 == "cancel" + MessageBox MB_ICONQUESTION|MB_YESNO|MB_TOPMOST \ + "Download of component '?sectionName' cancelled. Continue installation process??" \ + IDYES ignore + abort + ${Else} + MessageBox MB_ICONSTOP|MB_ABORTRETRYIGNORE|MB_TOPMOST "Error: $0" \ + IDRETRY retry IDIGNORE ignore + abort + ${EndIf} + + ; Shortcuts + # if d.len >= 6: + # let startMenuEntry = d[5] + # let e = splitFile(startMenuEntry).name.capitalizeAscii + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\?{e}.lnk" "$INSTDIR\?dir\?{startMenuEntry.toWin}" + !insertmacro MUI_STARTMENU_WRITE_END + # end if + + ignore: + SectionEnd + #end + +;-------------------------------- +; Section Descriptions + ; Series of strings describing each section + ; LangString DESC_CoreSection ${LANG_ENGLISH} "Core Nim files" + + ; The macros to actually insert the descriptions into the sections. + ; Each description above should have a corresponding MUI_DESCRIPTION_TEXT + ; macro linking the section to the description. + ; !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + ; !insertmacro MUI_DESCRIPTION_TEXT ${CoreSection} $(DESC_CoreSection) + ; !insertmacro MUI_FUNCTION_DESCRIPTION_END + + +;-------------------------------- +; Uninstaller Sections + + Section "Uninstall" + ; Remove previously created shortcuts + !insertmacro MUI_STARTMENU_GETFOLDER "Application" $ICONS_GROUP + Delete "$DESKTOP\?{c.displayName}.lnk" + + ; Remove installed application files + RMDir /r "$SMPROGRAMS\$ICONS_GROUP" + RMDir /r "$INSTDIR" + + ; Remove the previously created registry key + DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" + DeleteRegKey HKCU "${PRODUCT_DIR_REGKEY}" + SetAutoClose true + + ; Remove entries from the PATH environment variable + Push "$INSTDIR\bin" + Call un.RemoveFromPath + Push "$INSTDIR\dist\mingw" + Call un.RemoveFromPath + Push "$INSTDIR\dist\mingw\bin" + Call un.RemoveFromPath + Push "$PROFILE\.nimble\bin" + Call un.RemoveFromPath + SectionEnd + +;-------------------------------- +; Function hooks + + Function .onInit + ${GetRoot} "$EXEDIR" $R0 + strCpy $INSTDIR "$R0\?{c.name}" + FunctionEnd + + +;-------------------------------------------------------------------- +; Path functions +; +; Based on example from: +; http://nsis.sourceforge.net/Path_Manipulation +; +; Actually based on: +; https://www.smartmontools.org/browser/trunk/smartmontools/os_win32/installer.nsi#L636 + + +!include "WinMessages.nsh" + +; Registry Entry for environment (NT4,2000,XP) +; All users: +;!define Environ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' +; Current user only: +!define Environ 'HKCU "Environment"' + + +; AddToPath - Appends dir to PATH +; (does not work on Win9x/ME) +; +; Usage: +; Push "dir" +; Call AddToPath + +Function AddToPath + Exch $0 + Push $1 + Push $2 + Push $3 + Push $4 + + ; NSIS ReadRegStr returns empty string on string overflow + ; Native calls are used here to check actual length of PATH + + ; $4 = RegOpenKey(HKEY_CURRENT_USER, "Environment", &$3) + System::Call "advapi32::RegOpenKey(i 0x80000001, t'Environment', *i.r3) i.r4" + IntCmp $4 0 0 done done + ; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2)) + ; RegCloseKey($3) + System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4" + System::Call "advapi32::RegCloseKey(i $3)" + + IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA + DetailPrint "AddToPath: original length $2 > ${NSIS_MAX_STRLEN}" + MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}" + Goto done + + IntCmp $4 0 +5 ; $4 != NO_ERROR + IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND + DetailPrint "AddToPath: unexpected error code $4" + Goto done + StrCpy $1 "" + + ; Check if already in PATH + Push "$1;" + Push "$0;" + Call StrStr + Pop $2 + StrCmp $2 "" 0 done + Push "$1;" + Push "$0\;" + Call StrStr + Pop $2 + StrCmp $2 "" 0 done + + ; Prevent NSIS string overflow + StrLen $2 $0 + StrLen $3 $1 + IntOp $2 $2 + $3 + IntOp $2 $2 + 2 ; $2 = strlen(dir) + strlen(PATH) + sizeof(";") + IntCmp $2 ${NSIS_MAX_STRLEN} +4 +4 0 + DetailPrint "AddToPath: new length $2 > ${NSIS_MAX_STRLEN}" + MessageBox MB_OK "PATH not updated, new length $2 > ${NSIS_MAX_STRLEN}." + Goto done + + ; Append dir to PATH + DetailPrint "Add to PATH: $0" + StrCpy $2 $1 1 -1 + StrCmp $2 ";" 0 +2 + StrCpy $1 $1 -1 ; remove trailing ';' + StrCmp $1 "" +2 ; no leading ';' + StrCpy $0 "$1;$0" + WriteRegExpandStr ${Environ} "PATH" $0 + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + +done: + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + + +; RemoveFromPath - Removes dir from PATH +; +; Usage: +; Push "dir" +; Call RemoveFromPath + +Function un.RemoveFromPath + Exch $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + + ReadRegStr $1 ${Environ} "PATH" + StrCpy $5 $1 1 -1 + StrCmp $5 ";" +2 + StrCpy $1 "$1;" ; ensure trailing ';' + Push $1 + Push "$0;" + Call un.StrStr + Pop $2 ; pos of our dir + StrCmp $2 "" done + + DetailPrint "Remove from PATH: $0" + StrLen $3 "$0;" + StrLen $4 $2 + StrCpy $5 $1 -$4 ; $5 is now the part before the path to remove + StrCpy $6 $2 "" $3 ; $6 is now the part after the path to remove + StrCpy $3 "$5$6" + StrCpy $5 $3 1 -1 + StrCmp $5 ";" 0 +2 + StrCpy $3 $3 -1 ; remove trailing ';' + WriteRegExpandStr ${Environ} "PATH" $3 + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + +done: + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + + +; StrStr - find substring in a string +; +; Usage: +; Push "this is some string" +; Push "some" +; Call StrStr +; Pop $0 ; "some string" + +!macro StrStr un +Function ${un}StrStr + Exch $R1 ; $R1=substring, stack=[old$R1,string,...] + Exch ; stack=[string,old$R1,...] + Exch $R2 ; $R2=string, stack=[old$R2,old$R1,...] + Push $R3 + Push $R4 + Push $R5 + StrLen $R3 $R1 + StrCpy $R4 0 + ; $R1=substring, $R2=string, $R3=strlen(substring) + ; $R4=count, $R5=tmp + loop: + StrCpy $R5 $R2 $R3 $R4 + StrCmp $R5 $R1 done + StrCmp $R5 "" done + IntOp $R4 $R4 + 1 + Goto loop +done: + StrCpy $R1 $R2 "" $R4 + Pop $R5 + Pop $R4 + Pop $R3 + Pop $R2 + Exch $R1 ; $R1=old$R1, stack=[result,...] +FunctionEnd +!macroend +!insertmacro StrStr "" +!insertmacro StrStr "un." diff --git a/tools/niminst/setup.ico b/tools/niminst/setup.ico new file mode 100644 index 000000000..867163046 --- /dev/null +++ b/tools/niminst/setup.ico Binary files differdiff --git a/tools/niminst/uninstall.ico b/tools/niminst/uninstall.ico new file mode 100644 index 000000000..aff054644 --- /dev/null +++ b/tools/niminst/uninstall.ico Binary files differ |