diff --git a/CMakeLists.txt b/CMakeLists.txt index 8df0fe907..bbea1cc91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,5 +111,38 @@ set(CPACK_RPM_PACKAGE_ARCHITECTURE "${MOZART_PROP_PLATFORM_ARCH}") set(CPACK_RPM_PACKAGE_REQUIRES "tcl >= 8.5, tk >= 8.5, emacs") set(CPACK_RPM_PACKAGE_GROUP "Development/Languages") +# Configuration of Inno Setup files + +if(WIN32) + find_program(ISS_COMPILER NAMES iscc ISCC + HINTS "C:/Program Files (x86)/Inno Setup 5" "C:/Program Files/Inno Setup 5") + + if(NOT ISS_COMPILER) + message(WARNING "Inno Setup Compiler not found. You won't be able to build setup files.") + else() + message(STATUS "Using Inno Setup Compiler from: ${ISS_COMPILER}") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/distrib/windows" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/distrib") + + # Compute Tcl/Tk install path + get_filename_component(ISS_TCL_EXEC_PARENT "${ISS_TCL_EXEC}" DIRECTORY) + get_filename_component(ISS_TCL_PATH "${ISS_TCL_EXEC_PARENT}" DIRECTORY) + + # Compute emacs install path + get_filename_component(ISS_EMACS_EXEC_PARENT "${ISS_EMACS_EXEC}" DIRECTORY) + get_filename_component(ISS_EMACS_PATH "${ISS_EMACS_EXEC_PARENT}" DIRECTORY) + + # Parse Inno Setup config file + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/distrib/windows/MozartConfig.iss" + "${CMAKE_CURRENT_BINARY_DIR}/distrib/windows/MozartConfig.iss") + + # Add installer target + add_custom_target(installer + COMMAND ${CMAKE_MAKE_PROGRAM} install + COMMAND ${ISS_COMPILER} "${CMAKE_CURRENT_BINARY_DIR}/distrib/windows/MozartSetup.iss" + VERBATIM) + endif() +endif() + # Finally include CPack include(CPack) diff --git a/README.Windows.md b/README.Windows.md index 984b1981c..2d951da6e 100644 --- a/README.Windows.md +++ b/README.Windows.md @@ -11,6 +11,7 @@ Download and install the following tools: * Python (required for building LLVM) * Git for Windows * CMake >= 2.8.6 +* [Inno Setup](http://www.jrsoftware.org/isdl.php) (with preprocessor, required for building setup files) * A recent 32-bit (i686) or 64-bit (x86_64) targeting [MinGW-64 distro](http://mingw-w64.sourceforge.net/download.php#mingw-builds) with gcc >= 4.7.1. These tools won't be needed at run time. We will refer to the installation directory of MinGW (in which the first `bin` subdirectory is found) by ``. @@ -118,3 +119,16 @@ For Mozart to run properly, you need to ensure : * Tcl/Tk is in your PATH or its `lib` and `bin` subfolders are merged with Mozart ones * An environment variable `OZEMACS` is set to `\bin\runemacs.exe` + +## Making Mozart 2 packages + +If you want to build setup files for Mozart, just type in your terminal : + + C:> mingw32-make installer + +The new setup file will be located in your build directory. Two more CMake options are then available : + +* `-DISS_INCLUDE_EMACS=ON` will include your Emacs files in the package. +* `-DISS_INCLUDE_TCL=ON` will include your Tcl/Tk files in the package. + +Please note that ActiveTcl is not redistributable without an OEM license. diff --git a/distrib/windows/ModifyPath.iss b/distrib/windows/ModifyPath.iss new file mode 100644 index 000000000..5febd5d5e --- /dev/null +++ b/distrib/windows/ModifyPath.iss @@ -0,0 +1,185 @@ +[Code] + +// Version log: +// 03/31/2003: Initial release (thv(at)lr.dk) + +const + // Modification method + pmAddToBeginning = $1; // Add dir to beginning of Path + pmAddToEnd = $2; // Add dir to end of Path + pmAddAllways = $4; // Add also if specified dir is already included in existing path + pmAddOnlyIfDirExists = $8; // Add only if specified dir actually exists + pmRemove = $10; // Remove dir from path + pmRemoveSubdirsAlso = $20; // Remove dir and all subdirs from path + + // Scope + psCurrentUser = 1; // Modify path for current user + psAllUsers = 2; // Modify path for all users + + // Error results + mpOK = 0; // No errors + mpMissingRights = -1; // User has insufficient rights + mpAutoexecNoWriteacc = -2; // Autoexec can not be written (may be readonly) + mpBothAddAndRemove = -3; // User has specified that dir should both be removed from and added to path + + +{ Helper function: Split a path environment variable into individual dirnames } +function SplitPath(Path: string): TStringList ; +var + Pos: Integer; + S: string; +begin + Result := TStringList.Create; + S := ''; + for Pos :=1 to Length(Path) do + begin + if Path[Pos] <> ';' then + S := S + Path[Pos]; + if (Path[Pos] = ';') or (Pos = Length(Path)) then + begin + S := Trim(s); + S := RemoveQuotes(s); + S := Trim(s); + if S <> '' then + Result.Add(S); + S := ''; + end; + end; +end; // function SplitPath + + +{ Helper procedure: Concatenate individual dirnames into a path environment variable } +function ConcatPath(Dirs: TStringList; Quotes: Boolean): string; +var + Index, MaxIndex: Integer; + S: string; +begin + MaxIndex := Dirs.Count-1; + Result := ''; + for Index := 0 to MaxIndex do + begin + S := Dirs.Strings[Index]; + if Quotes and (pos(' ', S) > 0) then + S := AddQuotes(S); + Result := Result + S; + if Index < MaxIndex then + Result := Result + ';' + end; +end; // function ConcatPath + + +{ Helper function: Modifies path environment string } +function ModifyPathString(OldPath: string; DirName: string; Method: Integer; Quotes: Boolean): string; +var + Dirs: TStringList; + DirNotInPath: Boolean; + I: Integer; +begin + // Remove quotes form DirName + DirName := Trim(DirName); + DirName := RemoveQuotes(DirName); + DirName := Trim(DirName); + + // Split old path in individual directory names + Dirs := SplitPath(OldPath); + + // Check if dir is already in path + DirNotInPath := True; + for I:=0 to Dirs.Count-1 do + begin + if AnsiUpperCase(Dirs.Strings[I]) = AnsiUpperCase(DirName) then + DirNotInPath := False; + end; + + // Should dir be removed from existing Path? + if (Method and (pmRemove or pmRemoveSubdirsAlso)) > 0 then + begin + for I:=0 to Dirs.Count-1 do + begin + if (((Method and pmRemoveSubdirsAlso) > 0) and (pos(AnsiUpperCase(DirName) + '', AnsiUpperCase(Dirs.Strings[I])) = 1)) or + (((Method and (pmRemove) or (pmRemoveSubdirsAlso)) > 0) and (AnsiUpperCase(DirName) = AnsiUpperCase(Dirs.Strings[I]))) + then + Dirs.Delete(I); + end; + end; + + // Should dir be added to existing Path? + if (Method and (pmAddToBeginning or pmAddToEnd)) > 0 then + begin + // Add dir to path + if ((Method and pmAddAllways) > 0) or DirNotInPath then + begin + // Dir is not in path already or should be added anyway + if ((Method and pmAddOnlyIfDirExists) = 0) or DirExists(DirName) then + begin + // Dir actually exists or should be added anyway + if (Method and pmAddToBeginning) > 0 then + Dirs.Insert(0, DirName) + else + Dirs.Append(DirName); + end; + end; + end; + + // Concatenate directory names into one single path variable + Result := ConcatPath(Dirs, Quotes); + // Finally free Dirs object + Dirs.Free; +end; // function ModifyPathString + +{ Main function: Modify path } +function ModifyPath(Path: string; Method: Integer; Scope: Integer): Integer; +var + RegRootKey: Integer; + RegSubKeyName: string; + RegValueName: string; + OldPath, ResultPath: string; + OK: Boolean; +begin + // Check if both add and remove has been specified (= error!) + if (Method and (pmAddToBeginning or pmAddToEnd) and (pmRemove or pmRemoveSubdirsAlso)) > 0 then + begin + Result := mpBothAddAndRemove; + Exit; + end; + + // Perform directory constant expansion + Path := ExpandConstantEx(Path, ' ', ' '); + + // Initialize registry key and value names to reflect if changes should be global or local to current user only + case Scope of + psCurrentUser: + begin + RegRootKey := HKEY_CURRENT_USER; + RegSubKeyName := 'Environment'; + RegValueName := 'Path'; + end; + psAllUsers: + begin + RegRootKey := HKEY_LOCAL_MACHINE; + RegSubKeyName := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; + RegValueName := 'Path'; + end; + end; + + // Read current path value from registry + OK := RegQueryStringValue(RegRootKey, RegSubKeyName, RegValueName, OldPath); + if not OK and (Scope = psAllUsers) then + begin + Result := mpMissingRights; + Exit; + end; + + // Modify path + ResultPath := ModifyPathString(OldPath, Path, Method, False); + + // Write new path value to registry + if not RegWriteStringValue(RegRootKey, RegSubKeyName, RegValueName, ResultPath) then + begin + Result := mpMissingRights; + Exit; + end; + + // Expect everything to be OK + Result := mpOK; +end; // ModifyPath diff --git a/distrib/windows/MozartConfig.iss b/distrib/windows/MozartConfig.iss new file mode 100644 index 000000000..7768964dd --- /dev/null +++ b/distrib/windows/MozartConfig.iss @@ -0,0 +1,19 @@ +; This configuration file was generated by CMake +#define OutputFilename "${CPACK_PACKAGE_FILE_NAME}" + +; Mozart config +#define MozartFolder "${CMAKE_INSTALL_PREFIX}" +#define MozartVersion "${MOZART_PROP_OZ_VERSION}" +#define LicenseFile "${CMAKE_SOURCE_DIR}/LICENSE.txt" + +; Tcl/Tk config +#define NeededTclVersion "'${ISS_TCL_VERSION}'" +#define TclFolder "${ISS_TCL_PATH}" +#define TclIncluded "${ISS_INCLUDE_TCL}" + +; Emacs config +#define EmacsFolder "${ISS_EMACS_PATH}" +#define EmacsIncluded "${ISS_INCLUDE_EMACS}" + +; Target architecture can be x86 and/or x64 +#define TargetArch "${MOZART_PROP_PLATFORM_ARCH}" diff --git a/distrib/windows/MozartSetup.iss b/distrib/windows/MozartSetup.iss new file mode 100644 index 000000000..1c9b25c12 --- /dev/null +++ b/distrib/windows/MozartSetup.iss @@ -0,0 +1,115 @@ +#include "MozartConfig.iss" + +[Setup] +; NOTE: The value of AppId uniquely identifies this application. +; Do not use the same AppId value in installers for other applications. +; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) +AppId={{992C8269-AE73-4377-88BE-D92459001279} +AppName=Mozart +AppVersion={#MozartVersion} +AppPublisher=Universite catholique de Louvain +AppPublisherURL=http://mozart.github.io/ +AppSupportURL=http://mozart.github.io/ +AppUpdatesURL=http://mozart.github.io/ +DefaultDirName={pf}\Mozart +DefaultGroupName=Mozart +AllowNoIcons=yes +LicenseFile={#LicenseFile} +OutputDir=..\.. +OutputBaseFilename={#OutputFilename} +Compression=lzma +SolidCompression=yes +WizardImageFile=mozartside.bmp +WizardSmallImageFile=mozartsmall.bmp +ChangesEnvironment=yes +#if TargetArch == "x86_64" +ArchitecturesAllowed=x64 +ArchitecturesInstallIn64BitMode=x64 +#endif + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + + +[Components] +Name: "main"; Description: "Oz engine"; Types: full compact custom; Flags: fixed +#if EmacsIncluded == "ON" +Name: "emacs"; Description: "Emacs editor (for interactive Oz)"; Types: full +#endif + + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked + +[Files] +Source: "{#MozartFolder}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: main +#if TclIncluded == "ON" +Source: "{#TclFolder}\bin\*"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs; Components: main +Source: "{#TclFolder}\lib\*"; DestDir: "{app}\lib"; Flags: ignoreversion recursesubdirs; Components: main +#else +Source: "TclVer.bat"; Flags: dontcopy +#endif +#if EmacsIncluded == "ON" +Source: "{#EmacsFolder}\*"; DestDir: "{app}\opt"; Flags: ignoreversion recursesubdirs; Components: emacs +#endif + +[Icons] +Name: "{group}\Mozart Programming Interface"; Filename: "{app}\bin\oz.exe"; IconFilename: "{app}\bin\oz.exe" +Name: "{group}\{cm:UninstallProgram,Mozart}"; Filename: "{uninstallexe}" +Name: "{commondesktop}\Mozart Programming Interface"; Filename: "{app}\bin\oz.exe"; Tasks: desktopicon; IconFilename: "{app}\bin\oz.exe" + +[Registry] +#if EmacsIncluded == "ON" +Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "OZEMACS"; ValueData: "{app}\opt\bin\runemacs.exe"; Flags: uninsdeletevalue; Components:emacs +#endif +Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "OZHOME"; ValueData: "{app}"; Flags: uninsdeletevalue + +[Code] +#include "ModifyPath.iss" + +procedure CurStepChanged(CurStep: TSetupStep); +begin + case CurStep of + ssPostInstall: + begin + if IsAdminLoggedOn then + ModifyPath('{app}\bin', pmAddToEnd, psAllUsers) + else + ModifyPath('{app}\bin', pmAddToEnd, psCurrentUser); + end; + end; +end; + +procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); +begin + case CurUninstallStep of + usPostUninstall: + begin + if IsAdminLoggedOn then + ModifyPath('{app}\bin', pmRemove, psAllUsers) + else + ModifyPath('{app}\bin', pmRemove, psCurrentUser); + end; + end; +end; + +#if !(TclIncluded == "ON") +function PrepareToInstall(var NeedsRestart: Boolean): String; +var + TmpFileName, ExecStdout: string; + ResultCode: integer; + Version : string; +begin + ExtractTemporaryFile('TclVer.bat'); + TmpFileName := ExpandConstant('{tmp}') + '\tclver.txt'; + Exec(ExpandConstant('{tmp}') + '\TclVer.bat', '> "' + TmpFileName + '"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode); + LoadStringFromFile(TmpFileName, ExecStdout); + Version := Trim(ExecStdout); + if Version = '0.0' then + MsgBox('Tcl/Tk ' + {#NeededTclVersion} + ' seems not to be installed or not to be in your PATH. Mozart may not work properly.', mbError, MB_OK) + else if not (Version = {#NeededTclVersion}) then + MsgBox('Tcl/Tk ' + Version + ' was found, but version ' + {#NeededTclVersion} + ' is required. Mozart may not work properly.', mbError, MB_OK); + DeleteFile(TmpFileName); + Result := ''; +end; +#endif diff --git a/distrib/windows/TclVer.bat b/distrib/windows/TclVer.bat new file mode 100644 index 000000000..aaf682a38 --- /dev/null +++ b/distrib/windows/TclVer.bat @@ -0,0 +1,8 @@ +@echo off +set PATH=%PATH%;%Systemdrive%\Tcl\bin +set CMD=tclsh + +where %CMD% 1>NUL 2>NUL +if %ERRORLEVEL% neq 0 ( +echo 0.0 +) else echo puts $tcl_version;exit 0 | %CMD% 2>NUL diff --git a/distrib/windows/mozartside.bmp b/distrib/windows/mozartside.bmp new file mode 100644 index 000000000..cae100613 Binary files /dev/null and b/distrib/windows/mozartside.bmp differ diff --git a/distrib/windows/mozartsmall.bmp b/distrib/windows/mozartsmall.bmp new file mode 100644 index 000000000..4b04ae019 Binary files /dev/null and b/distrib/windows/mozartsmall.bmp differ diff --git a/opi/emacs/CMakeLists.txt b/opi/emacs/CMakeLists.txt index c527ad71f..d7a61d3d6 100644 --- a/opi/emacs/CMakeLists.txt +++ b/opi/emacs/CMakeLists.txt @@ -9,6 +9,12 @@ if(NOT EMACS) message(WARNING "Emacs executable not found, cannot build .el files") else() message(STATUS "Using emacs from: ${EMACS}") + + if(WIN32) + # Path for Inno Setup + set(ISS_EMACS_EXEC ${EMACS} CACHE STRING "Executable path of emacs") + endif() + set(EMACS_OPTIONS -q -batch -f batch-byte-compile) set(EMACS_MODE_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/oz.el" diff --git a/wish/CMakeLists.txt b/wish/CMakeLists.txt index ee07efa0c..363432c4d 100644 --- a/wish/CMakeLists.txt +++ b/wish/CMakeLists.txt @@ -2,12 +2,15 @@ cmake_minimum_required(VERSION 2.8.6) project(MOZARTWISH) find_package(TCL REQUIRED) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_INTERP_RESULT=1") include_directories("${TCL_INCLUDE_PATH}" "${TK_INCLUDE_PATH}") if(WIN32) + # Path and version for Inno Setup + set(ISS_TCL_VERSION ${TCLSH_VERSION_STRING} CACHE STRING "Tcl version string") + set(ISS_TCL_EXEC ${TCL_TCLSH} CACHE STRING "Tclsh executable path") + set(MAIN_SRC winmain.cc) else() set(MAIN_SRC unixmain.cc)