From: Daira Hopwood <daira@jacaranda.org> Date: Tue, 21 Oct 2014 20:11:56 +0000 (+0100) Subject: Make a start on the Windows installer. X-Git-Url: https://git.rkrishnan.org/vdrive/index.html?a=commitdiff_plain;h=0d57ba4de0b1e3ac7aae0ae3bd271e6088070502;p=tahoe-lafs%2Ftahoe-lafs.git Make a start on the Windows installer. Signed-off-by: Daira Hopwood <daira@jacaranda.org> --- diff --git a/misc/build_helpers/windows/installer/.gitattributes b/misc/build_helpers/windows/installer/.gitattributes new file mode 100644 index 00000000..33ffd98d --- /dev/null +++ b/misc/build_helpers/windows/installer/.gitattributes @@ -0,0 +1,2 @@ +installer.sln -text +installer/installer.vcproj -text diff --git a/misc/build_helpers/windows/installer/installer.sln b/misc/build_helpers/windows/installer/installer.sln new file mode 100644 index 00000000..95d7a25d --- /dev/null +++ b/misc/build_helpers/windows/installer/installer.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "installer", "installer\installer.vcproj", "{413A02C3-FBCC-42B4-9277-F1CCBDD274CA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {413A02C3-FBCC-42B4-9277-F1CCBDD274CA}.Debug|Win32.ActiveCfg = Debug|Win32 + {413A02C3-FBCC-42B4-9277-F1CCBDD274CA}.Debug|Win32.Build.0 = Debug|Win32 + {413A02C3-FBCC-42B4-9277-F1CCBDD274CA}.Release|Win32.ActiveCfg = Release|Win32 + {413A02C3-FBCC-42B4-9277-F1CCBDD274CA}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/misc/build_helpers/windows/installer/installer/app.rc b/misc/build_helpers/windows/installer/installer/app.rc new file mode 100644 index 00000000..f82f4b65 --- /dev/null +++ b/misc/build_helpers/windows/installer/installer/app.rc @@ -0,0 +1,4 @@ +#include <windows.h> +#define IDC_STATIC -1 + +100 ICON "logo.ico" diff --git a/misc/build_helpers/windows/installer/installer/installer.cpp b/misc/build_helpers/windows/installer/installer/installer.cpp new file mode 100644 index 00000000..f372e47d --- /dev/null +++ b/misc/build_helpers/windows/installer/installer/installer.cpp @@ -0,0 +1,418 @@ +// installer.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" + +int wmain(int argc, wchar_t *argv[]); +wchar_t * get_default_destination_dir(); +void self_extract(wchar_t *destination_dir); +void empty_directory(wchar_t *destination_dir); +void unzip_from_executable(wchar_t *executable_path, wchar_t *destination_dir); +size_t read_uint32_le(unsigned char *b); +void unzip(wchar_t *zip_path, wchar_t *destination_dir); +void install_python(wchar_t *python_installer_dir); +void scriptsetup(wchar_t *destination_dir); +bool spawn_with_redirect(FILE *redirect, unsigned char *output_buf, size_t output_size, const wchar_t *argv[]); + +#define fail_unless(x, s) if (!(x)) { fail(s); } +void fail(char *s); +void warn(char *s); +void pause(); + +#define REQUIRED_PYTHON_VERSION_PREFIX "Python 2.7." +#define PYTHON_INSTALLER_FILESPEC L"python*.msi" + +// defines PKGNAME_AND_VERSION, written by setup.py +#include "_version.h" + + +void noop_handler(const wchar_t * expression, + const wchar_t * function, + const wchar_t * file, + unsigned int line, + uintptr_t pReserved) { +} + +int wmain(int argc, wchar_t *argv[]) { + _set_invalid_parameter_handler(noop_handler); + + if (argc >= 2 && wcscmp(argv[1], L"--help") == 0) { + printf("installer <destination_dir>\n"); + return 1; + } + wchar_t *destination_dir = (argc >= 2) ? argv[1] : get_default_destination_dir(); + + size_t len = wcslen(destination_dir); + // FIXME: strip trailing slash rather than rejecting it + fail_unless(len > 0 && destination_dir[len-1] != '\\' && len < MAX_PATH-1, "Invalid destination directory."); + + self_extract(destination_dir); + install_python(destination_dir); + scriptsetup(destination_dir); + pause(); + + return 0; +} + +wchar_t * get_default_destination_dir() { + // TODO: get Program Files directory from the registry + return L"C:\\Program Files\\Tahoe-LAFS"; +} + +void self_extract(wchar_t *destination_dir) { + wchar_t executable_path[MAX_PATH]; + + HMODULE hModule = GetModuleHandle(NULL); + fail_unless(hModule != NULL, "Could not get the module handle."); + GetModuleFileNameW(hModule, executable_path, MAX_PATH); + fail_unless(GetLastError() == ERROR_SUCCESS, "Could not get the path of the current executable."); + + empty_directory(destination_dir); + unzip_from_executable(executable_path, destination_dir); +} + +void empty_directory(wchar_t *destination_dir) { + // Create an empty directory at destination_dir. + errno = 0; + int res = _wmkdir(destination_dir); + fail_unless((res == 0 && errno == 0) || errno == EEXIST, "Could not create destination directory."); +} + +void unzip_from_executable(wchar_t *executable_path, wchar_t *destination_dir) { + // shell32's zipped folder implementation is strict about the zip format and + // does not support unzipping a self-extracting exe directly. So we copy the + // original zip file that was appended to the exe to a temporary directory, + // and use shell32 to unzip it from there. To get the length of the zip file, + // we look at its "end of central directory record", which is documented at + // <http://www.pkware.com/documents/casestudies/APPNOTE.TXT>. + // For simplicity we only handle the case of a zip file that has no archive + // comment, that does not use disk spanning, and that does not have a + // "Zip64 end of central directory record". + + // APPNOTE.TXT section 4.3.16. + const size_t sizeof_eocd = 22; + unsigned char end_data[sizeof_eocd]; + unsigned char eocd_signature[] = {0x50, 0x4B, 0x05, 0x06}; + unsigned char comment_length[] = {0x00, 0x00}; + unsigned char disk_num[] = {0x00, 0x00}; + + errno = 0; + FILE *f = _wfopen(executable_path, L"rb"); + fail_unless(f != NULL && errno == 0 && ferror(f) == 0, + "Could not open executable file."); + + fseek(f, -(off_t) sizeof_eocd, SEEK_END); + fail_unless(errno == 0 && ferror(f) == 0, + "Could not seek to end-of-central-directory record."); + + __int64 eocd_offset = _ftelli64(f); + fail_unless(errno == 0 && ferror(f) == 0 && eocd_offset >= 0, + "Could not read position of end-of-central-directory record."); + fail_unless(eocd_offset + sizeof_eocd <= 0xFFFFFFFFi64, + "Cannot read an executable file >= 4 GiB."); + + size_t n = fread(end_data, sizeof(end_data), 1, f); + fail_unless(n == 1 && errno == 0 && ferror(f) == 0, + "Could not read end records."); + + fail_unless(memcmp(end_data + sizeof(end_data) - sizeof(comment_length), + comment_length, sizeof(comment_length)) == 0, + "Cannot read a zip file that has an archive comment."); + + fail_unless(memcmp(end_data, eocd_signature, sizeof(eocd_signature)) == 0, + "Could not find the end-of-central-directory signature."); + + fail_unless(memcmp(end_data + 4, disk_num, sizeof(disk_num)) == 0 && + memcmp(end_data + 6, disk_num, sizeof(disk_num)) == 0, + "Cannot read a zipfile that spans disks."); + + size_t cd_length = read_uint32_le(end_data + 12); + size_t cd_offset = read_uint32_le(end_data + 16); + __int64 zip_length = (__int64) cd_offset + cd_length + sizeof_eocd; + fail_unless(zip_length <= 0x7FFFFFFFi64, + "Cannot copy a zip file >= 2 GiB."); + + fseek(f, -(off_t) zip_length, SEEK_END); + fail_unless(errno == 0 && ferror(f) == 0, + "Could not seek to start of embedded zip file."); + + const wchar_t tmp_filename[] = L"tahoe-lafs.zip"; // FIXME make this more unique. + wchar_t tmp_path[MAX_PATH]; + DWORD len = GetTempPathW(MAX_PATH, tmp_path); + fail_unless(len > 0, "Could not obtain temporary directory path."); + fail_unless(len < MAX_PATH - wcslen(tmp_filename), "Temporary directory path is too long."); + wcscpy(tmp_path + len, tmp_filename); + + errno = 0; + FILE *tmp_file = _wfopen(tmp_path, L"wb"); + fail_unless(tmp_file != NULL && errno == 0 && ferror(f) == 0, + "Could not open temporary zip file."); + + // FIXME: delete the temporary file if there is an error. + unsigned char buf[16384]; + size_t remaining_length = (size_t) zip_length; + while (remaining_length > 0) { + size_t chunk_length = min(remaining_length, sizeof(buf)); + n = fread(buf, chunk_length, 1, f); + fail_unless(n == 1 && errno == 0 && ferror(f) == 0, + "Could not read from executable file."); + fwrite(buf, chunk_length, 1, tmp_file); + fail_unless(n == 1 && errno == 0 && ferror(f) == 0, + "Could not write to temporary file."); + remaining_length -= chunk_length; + } + int res = fclose(tmp_file); + fail_unless(res == 0, "Could not close temporary zip file."); + fclose(f); // ignore errors + + unzip(tmp_path, destination_dir); + _wunlink(tmp_path); // ignore errors +} + +// read unsigned little-endian 32-bit integer +size_t read_uint32_le(unsigned char *b) { + return ((size_t) b[0] ) | + ((size_t) b[1] << 8) | + ((size_t) b[2] << 16) | + ((size_t) b[3] << 24); +} + +void unzip(wchar_t *zip_path, wchar_t *destination_dir) { + // Based loosely on + // <https://social.msdn.microsoft.com/Forums/vstudio/en-US/45668d18-2840-4887-87e1-4085201f4103/visual-c-to-unzip-a-zip-file-to-a-specific-directory?forum=vclanguage>. + + // CoInitializeEx: <http://msdn.microsoft.com/en-gb/library/windows/desktop/ms695279(v=vs.85).aspx> + HRESULT res = CoInitializeEx(NULL, 0); + fail_unless(res == S_OK || res == S_FALSE, "Could not initialize COM."); + + // SysAllocString: <http://msdn.microsoft.com/en-gb/library/windows/desktop/ms221458(v=vs.85).aspx> + // BSTR: <http://msdn.microsoft.com/en-us/library/windows/desktop/ms221069(v=vs.85).aspx> + + VARIANT zip_path_var; + zip_path_var.vt = VT_BSTR; + zip_path_var.bstrVal = SysAllocString(zip_path); + fail_unless(zip_path_var.bstrVal != NULL, "Could not allocate string for zip file path."); + + VARIANT destination_dir_var; + destination_dir_var.vt = VT_BSTR; + destination_dir_var.bstrVal = SysAllocString(destination_dir); + fail_unless(destination_dir_var.bstrVal != NULL, "Could not allocate string for destination directory path."); + + // CoCreateInstance: <http://msdn.microsoft.com/en-gb/library/windows/desktop/ms686615(v=vs.85).aspx> + IShellDispatch *shell; + res = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **) &shell); + fail_unless(res == S_OK, "Could not create Shell instance."); + + // Folder.NameSpace: <http://msdn.microsoft.com/en-gb/library/windows/desktop/gg537721(v=vs.85).aspx> + Folder *zip_folder = NULL; + res = shell->NameSpace(zip_path_var, &zip_folder); + fail_unless(res == S_OK && zip_folder != NULL, "Could not create zip Folder object."); + + Folder *destination_folder = NULL; + res = shell->NameSpace(destination_dir_var, &destination_folder); + fail_unless(res == S_OK && destination_folder != NULL, "Could not create destination Folder object."); + + FolderItems *zip_folderitems = NULL; + zip_folder->Items(&zip_folderitems); + fail_unless(zip_folderitems != NULL, "Could not create zip FolderItems object."); + + VARIANT zip_idispatch_var; + zip_idispatch_var.vt = VT_DISPATCH; + zip_idispatch_var.pdispVal = NULL; + zip_folderitems->QueryInterface(IID_IDispatch, (void **) &zip_idispatch_var.pdispVal); + fail_unless(zip_idispatch_var.pdispVal != NULL, "Could not create IDispatch for zip FolderItems object."); + + // Folder.CopyHere: <http://msdn.microsoft.com/en-us/library/ms723207(v=vs.85).aspx> + // (16) Respond with "Yes to All" for any dialog box that is displayed. + // (256) Display a progress dialog box but do not show the file names. + // (512) Do not confirm the creation of a new directory if the operation requires one to be created. + // (1024) Do not display a user interface if an error occurs. + // These options are ignored on Windows XP. + VARIANT options_var; + options_var.vt = VT_I4; + options_var.lVal = 16 | 256 | 512 | 1024; + + res = destination_folder->CopyHere(zip_idispatch_var, options_var); + fail_unless(res == S_OK, "Could not extract zip file contents to destination directory."); + + // We don't bother to free/release stuff unless we succeed, since we exit on failure. + + // SysFreeString: <http://msdn.microsoft.com/en-gb/library/windows/desktop/ms221481(v=vs.85).aspx> + SysFreeString(zip_path_var.bstrVal); + SysFreeString(destination_dir_var.bstrVal); + zip_idispatch_var.pdispVal->Release(); + zip_folderitems->Release(); + destination_folder->Release(); + zip_folder->Release(); + shell->Release(); + + // CoUninitialize: <http://msdn.microsoft.com/en-gb/library/windows/desktop/ms688715(v=vs.85).aspx> + CoUninitialize(); +} + +void install_python(wchar_t *python_installer_dir) { + printf("Checking for " REQUIRED_PYTHON_VERSION_PREFIX "..\n"); + + unsigned char output_buf[1024]; + const wchar_t *argv[] = { L"python", L"-V", NULL }; + bool res = spawn_with_redirect(stderr, output_buf, sizeof(output_buf), &argv[0]); + if (res) { + printf("Found %s", (char *) output_buf); + if (strncmp((char *) output_buf, REQUIRED_PYTHON_VERSION_PREFIX, strlen(REQUIRED_PYTHON_VERSION_PREFIX)) == 0) { + return; + } else { + printf("but we need a newer version.\n"); + } + } else { + printf("No Python found.\n"); + } + + wchar_t installer_pattern[MAX_PATH]; + int n = _snwprintf(installer_pattern, MAX_PATH, L"%ls\\%ls", python_installer_dir, PYTHON_INSTALLER_FILESPEC); + fail_unless(n >= 0 && n < MAX_PATH, "Could not construct wildcard path to Python installer."); + + WIN32_FIND_DATA find_data; + HANDLE search_handle = FindFirstFileW(installer_pattern, &find_data); + fail_unless(search_handle != INVALID_HANDLE_VALUE, "Could not find the Python installer.") + + wchar_t installer_path[MAX_PATH]; + n = _snwprintf(installer_path, MAX_PATH, L"%ls\\%ls", python_installer_dir, find_data.cFileName); + fail_unless(n >= 0 && n < MAX_PATH, "Could not construct path to Python installer."); + printf("%ls\n", installer_path); + + wchar_t installer_path_quoted[2+MAX_PATH]; + n = _snwprintf(installer_path_quoted, 2+MAX_PATH, L"\"%ls\"", installer_path); + fail_unless(n >= 0 && n < 2+MAX_PATH, "Could not construct quoted path to Python installer."); + printf("%ls\n", installer_path_quoted); + + // <https://www.python.org/download/releases/2.5/msi/> + // XXX I'm not sure whether or not we want "/passive". These options + // may silently remove a previous Python installation that was not + // detected by the check above, and we'd like that to prompt. + const wchar_t *python_installer_argv[] = { + L"msiexec", L"/i", installer_path_quoted, + L"/passive", L"/norestart", L"ALLUSERS=1", L"ADDLOCAL=Extensions", NULL + }; + errno = 0; + intptr_t exit_code = _wspawnvp(P_WAIT, python_installer_argv[0], python_installer_argv); + int saved_errno = errno; + + _wunlink(installer_path); // ignore errors + + fail_unless(saved_errno == 0, "Could not execute Python installer."); + fail_unless(exit_code == 0, "Python installer failed."); +} + +void scriptsetup(wchar_t *destination_dir) { + // 12 = length of --addpath="" + wchar_t addpath_option_quoted[12+MAX_PATH]; + int n = _snwprintf(addpath_option_quoted, 12+MAX_PATH, L"--addpath=\"%ls\\%ls\\bin\"", destination_dir, PKGNAME_AND_VERSION); + fail_unless(n >= 0 && n < 12+MAX_PATH, "Could not construct path for bin directory."); + + unsigned char output_buf[10240]; + const wchar_t *scriptsetup_argv[] = { + L"python", L"setup.py", L"scriptsetup", + L"--allusers", addpath_option_quoted, + NULL + }; + bool res = spawn_with_redirect(stdout, output_buf, sizeof(output_buf), &scriptsetup_argv[0]); + puts((char *) output_buf); + fail_unless(res, "Could not set up Python to run the 'tahoe' command."); +} + +bool spawn_with_redirect(FILE *redirect, unsigned char *output_buf, size_t output_size, const wchar_t *argv[]) { + bool result = false; + fail_unless(output_size > 0, "Invalid output_size."); + output_buf[0] = 0; + + // Redirection is annoyingly complicated. + int output_pipe[2]; + errno = 0; + int res = _pipe(output_pipe, 512, _O_BINARY | _O_NOINHERIT); + if (res != 0) { + warn("Could not create pipe."); + return false; + } + int output_read_fd = output_pipe[0], output_write_fd = output_pipe[1]; + + // Duplicate the redirected file descriptor (the call to _dup2 will close the original). + int original_fd = _dup(_fileno(redirect)); + if (errno != 0) { + warn("Could not duplicate original file descriptor."); + return false; + } + + // Duplicate write end of pipe to redirected file descriptor. + res = _dup2(output_write_fd, _fileno(redirect)); + if (res != 0 || errno != 0) { + warn("Could not redirect."); + return false; + } + + // Close original file descriptor for write end of pipe. + _close(output_write_fd); // ignore errors + + HANDLE process_handle = (HANDLE) _wspawnvp(P_NOWAIT, argv[0], argv); + if (process_handle == (HANDLE) -1) { + warn("Could not execute subprocess."); + } + + // Duplicate copy of original stdout back into stdout. + errno = 0; + res = _dup2(original_fd, _fileno(redirect)); + fail_unless(res == 0 && errno == 0, "Could not restore stdout."); + + // Close duplicate copy of original fd. + _close(original_fd); // ignore errors + + if (process_handle == (HANDLE) -1) { + return false; + } + + DWORD exit_code = 0; + errno = 0; + unsigned char *p = output_buf; + size_t remaining_size = output_size; + int bytes_read; + do { + if (remaining_size == 0) { + bytes_read = 0; + Sleep(100); + } else { + bytes_read = _read(output_read_fd, p, remaining_size-1); + if (errno != 0 || bytes_read < 0) { + warn("Could not read from subprocess output."); + return false; + } + fail_unless((size_t) bytes_read < output_size, "Unexpectedly long read."); + p += bytes_read; + remaining_size -= bytes_read; + *p = 0; + } + + // GetExitCodeProcess: <http://msdn.microsoft.com/en-gb/library/windows/desktop/ms683189(v=vs.85).aspx> + BOOL res = GetExitCodeProcess(process_handle, &exit_code); + if (!res) { + warn("Could not get subprocess exit code."); + return false; + } + } while (bytes_read > 0 && exit_code == STILL_ACTIVE); + + return (exit_code == 0); +} + +void fail(char *s) { + // TODO: show dialog box + fprintf(stderr, "%s\n", s); + pause(); + exit(1); +} + +void warn(char *s) { + fprintf(stderr, "%s\n", s); +} + +void pause() { + printf("Press any key to finish.\n"); + _getch(); +} \ No newline at end of file diff --git a/misc/build_helpers/windows/installer/installer/installer.vcproj b/misc/build_helpers/windows/installer/installer/installer.vcproj new file mode 100644 index 00000000..a2699a2b --- /dev/null +++ b/misc/build_helpers/windows/installer/installer/installer.vcproj @@ -0,0 +1,237 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9.00" + Name="installer" + ProjectGUID="{413A02C3-FBCC-42B4-9277-F1CCBDD274CA}" + RootNamespace="installer" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="2" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + UACExecutionLevel="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + VerboseOutput="true" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + Description="Making self-extracting executable..." + CommandLine="cmd /c "copy /y /b $(OutDir)\$(ProjectName).exe+C:\tahoe\allmydata-tahoe-1.10.0.zip $(OutDir)\$(ProjectName).exe"" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="2" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="2" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\installer.cpp" + > + </File> + <File + RelativePath=".\stdafx.cpp" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="1" + /> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="1" + /> + </FileConfiguration> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath=".\_version.h" + > + </File> + <File + RelativePath=".\stdafx.h" + > + </File> + <File + RelativePath=".\targetver.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + <File + RelativePath=".\app.rc" + > + </File> + <File + RelativePath=".\logo.ico" + > + </File> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/misc/build_helpers/windows/installer/installer/logo.ico b/misc/build_helpers/windows/installer/installer/logo.ico new file mode 100644 index 00000000..84656706 Binary files /dev/null and b/misc/build_helpers/windows/installer/installer/logo.ico differ diff --git a/misc/build_helpers/windows/installer/installer/logo.svg b/misc/build_helpers/windows/installer/installer/logo.svg new file mode 100644 index 00000000..6de798e3 --- /dev/null +++ b/misc/build_helpers/windows/installer/installer/logo.svg @@ -0,0 +1,157 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:h="http://www.w3.org/1999/xhtml" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="256" + height="256" + preserveAspectRatio="xMidYMid" + viewBox="-180 -180 256 256" + id="svg3480" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="logo.svg"> + <metadata + id="metadata3511"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title>Tahoe-LAFS logo</dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="784" + inkscape:window-height="765" + id="namedview3509" + showgrid="false" + inkscape:zoom="0.921875" + inkscape:cx="128" + inkscape:cy="128" + inkscape:window-x="0" + inkscape:window-y="25" + inkscape:window-maximized="0" + inkscape:current-layer="svg3480" /> + <style + type="text/css" + id="style3482"> + .storage { + fill: black; + stroke: none; + } + .arrow { + stroke: black; + stroke-width: 2; + marker-end: url(#arrowhead); + } +</style> + <title + id="title3484">Tahoe-LAFS logo</title> + <desc + id="desc3486"> + <h:p>A proposed logo for the Tahoe-LAFS Project.</h:p> + <h:p><h:a + rel="license" + href="http://creativecommons.org/licenses/by/3.0/"> + <h:img + alt="Creative Commons License" + style="border-width:0" + src="http://i.creativecommons.org/l/by/3.0/88x31.png" /> +</h:a> +<h:br /> +<h:span + href="http://purl.org/dc/dcmitype/StillImage" + property="dct:title" + rel="dct:type">Tahoe-LAFS Logo</h:span> + by <h:a + href="http://switchb.org/kpreid/2009/tahoe/" + property="cc:attributionName" + rel="cc:attributionURL">Kevin Reid</h:a> + is licensed under a <h:a + rel="license" + href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</h:a> +.</h:p> + </desc> + <defs + id="defs3488"> + <marker + id="arrowhead" + viewBox="0 -5 10 10" + refX="2" + refY="0" + markerUnits="userSpaceOnUse" + markerWidth="10" + markerHeight="10" + orient="auto"> + <path + d="M 0 -3 L 9 0 L 0 3 z" + id="path3491" /> + </marker> + </defs> + <g + transform="translate(-48 28) scale(3.4)" + id="g3493"> + <line + class="arrow" + x1="0" + y1="0" + x2="0" + y2="-35" + id="line3495" /> + <line + class="arrow" + x1="0" + y1="0" + x2="-8" + y2="-21" + id="line3497" /> + <line + class="arrow" + x1="0" + y1="0" + x2="8" + y2="-21" + id="line3499" /> + <circle + fill="red" + r="10" + id="circle3501" + d="M 10,0 C 10,5.5228475 5.5228475,10 0,10 -5.5228475,10 -10,5.5228475 -10,0 c 0,-5.5228475 4.4771525,-10 10,-10 5.5228475,0 10,4.4771525 10,10 z" /> + <circle + class="storage" + r="8" + cy="-50" + id="circle3503" + d="m 8,-50 c 0,4.418278 -3.581722,8 -8,8 -4.418278,0 -8,-3.581722 -8,-8 0,-4.418278 3.581722,-8 8,-8 4.418278,0 8,3.581722 8,8 z" /> + <circle + class="storage" + r="8" + cy="-35" + cx="-14" + id="circle3505" + d="m -6,-35 c 0,4.418278 -3.581722,8 -8,8 -4.418278,0 -8,-3.581722 -8,-8 0,-4.418278 3.581722,-8 8,-8 4.418278,0 8,3.581722 8,8 z" /> + <circle + class="storage" + r="8" + cy="-35" + cx="14" + id="circle3507" + d="m 22,-35 c 0,4.418278 -3.581722,8 -8,8 -4.418278,0 -8,-3.581722 -8,-8 0,-4.418278 3.581722,-8 8,-8 4.418278,0 8,3.581722 8,8 z" /> + </g> +</svg> diff --git a/misc/build_helpers/windows/installer/installer/make-icon.sh b/misc/build_helpers/windows/installer/installer/make-icon.sh new file mode 100755 index 00000000..987e0ecb --- /dev/null +++ b/misc/build_helpers/windows/installer/installer/make-icon.sh @@ -0,0 +1,23 @@ +#! /bin/bash +# Based on <http://stackoverflow.com/a/23688878/393146> +# converts the passed-in svgs to ico + +if [[ $# -eq 0 ]]; then + echo "Usage: $0 svg1 [svg2 [...]]" + exit 0 +fi + +temp=$(mktemp -d) +declare -a res=(16 24 32 48 64 256) +for f in $*; do + mkdir -p $temp/$(dirname $f) + for r in "${res[@]}"; do + inkscape -z -e $temp/${f}${r}.png -w $r -h $r $f + done + resm=( "${res[@]/#/$temp/$f}" ) + resm=( "${resm[@]/%/.png}" ) + for filetype in ico; do + convert "${resm[@]}" ${f%%.*}.$filetype + done +done +rm -rf $temp diff --git a/misc/build_helpers/windows/installer/installer/stdafx.cpp b/misc/build_helpers/windows/installer/installer/stdafx.cpp new file mode 100644 index 00000000..c1026abd --- /dev/null +++ b/misc/build_helpers/windows/installer/installer/stdafx.cpp @@ -0,0 +1,5 @@ +// stdafx.cpp : source file that includes just the standard includes +// installer.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" diff --git a/misc/build_helpers/windows/installer/installer/stdafx.h b/misc/build_helpers/windows/installer/installer/stdafx.h new file mode 100644 index 00000000..9f8375b2 --- /dev/null +++ b/misc/build_helpers/windows/installer/installer/stdafx.h @@ -0,0 +1,29 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include <stdio.h> +#include <wchar.h> +#include <stdlib.h> +#include <errno.h> +#include <io.h> +#include <fcntl.h> +#include <process.h> +#include <sys/stat.h> +#include <conio.h> + +// Turn off the warnings nagging you to use the more complicated *_s +// "secure" functions that are actually more difficult to use securely. +#pragma warning(disable:4996) + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <wtypes.h> +#include <objbase.h> +#include <shldisp.h> +#include <shellapi.h> diff --git a/misc/build_helpers/windows/installer/installer/targetver.h b/misc/build_helpers/windows/installer/installer/targetver.h new file mode 100644 index 00000000..6fe8eb79 --- /dev/null +++ b/misc/build_helpers/windows/installer/installer/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + diff --git a/setuptools-0.6c16dev6.egg/setuptools/command/scriptsetup.py b/setuptools-0.6c16dev6.egg/setuptools/command/scriptsetup.py index db68c07a..e4dd9dd0 100644 --- a/setuptools-0.6c16dev6.egg/setuptools/command/scriptsetup.py +++ b/setuptools-0.6c16dev6.egg/setuptools/command/scriptsetup.py @@ -1,6 +1,6 @@ from distutils.errors import DistutilsSetupError from setuptools import Command -import sys +import sys, os class scriptsetup(Command): action = (sys.platform == "win32" @@ -10,11 +10,14 @@ class scriptsetup(Command): user_options = [ ('allusers', 'a', 'make changes for all users of this Windows installation (requires Administrator privileges)'), + ('addpath=', 'p', + 'add a directory to the PATH (user by default; system if --allusers is specified)'), ] boolean_options = ['allusers'] def initialize_options(self): self.allusers = False + self.addpath = None def finalize_options(self): pass @@ -23,11 +26,13 @@ class scriptsetup(Command): if sys.platform != "win32": print "\n'scriptsetup' isn't needed on non-Windows platforms." else: - do_scriptsetup(self.allusers) + do_scriptsetup(self.allusers, self.addpath) -def do_scriptsetup(allusers=False): +def do_scriptsetup(allusers=False, addpath=None): print "\nSetting up environment to run scripts for %s..." % (allusers and "all users" or "the current user") + if addpath: + print "%r will be added to the PATH." % (addpath,) from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_CLASSES_ROOT, \ REG_SZ, REG_EXPAND_SZ, KEY_QUERY_VALUE, KEY_SET_VALUE, \ @@ -241,13 +246,15 @@ def do_scriptsetup(allusers=False): except Exception, e: print "Warning: %r" % (e,) - changed_assoc = associate(".pyscript", "Python.File", allusers) changed_env = False try: changed_env |= add_to_environment("PATHEXT", ".pyscript", allusers) changed_env |= add_to_environment("PATHEXT", ".pyw", allusers) + if addpath: + abs_path = os.path.abspath(os.path.normpath(addpath)) + changed_env |= add_to_environment("PATH", abs_path, allusers) finally: CloseKey(user_env) CloseKey(system_env)