]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
Make a start on the Windows installer.
authorDaira Hopwood <daira@jacaranda.org>
Tue, 21 Oct 2014 20:11:56 +0000 (21:11 +0100)
committerDaira Hopwood <daira@jacaranda.org>
Thu, 30 Apr 2015 14:17:27 +0000 (15:17 +0100)
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
12 files changed:
misc/build_helpers/windows/installer/.gitattributes [new file with mode: 0644]
misc/build_helpers/windows/installer/installer.sln [new file with mode: 0644]
misc/build_helpers/windows/installer/installer/app.rc [new file with mode: 0644]
misc/build_helpers/windows/installer/installer/installer.cpp [new file with mode: 0644]
misc/build_helpers/windows/installer/installer/installer.vcproj [new file with mode: 0644]
misc/build_helpers/windows/installer/installer/logo.ico [new file with mode: 0644]
misc/build_helpers/windows/installer/installer/logo.svg [new file with mode: 0644]
misc/build_helpers/windows/installer/installer/make-icon.sh [new file with mode: 0755]
misc/build_helpers/windows/installer/installer/stdafx.cpp [new file with mode: 0644]
misc/build_helpers/windows/installer/installer/stdafx.h [new file with mode: 0644]
misc/build_helpers/windows/installer/installer/targetver.h [new file with mode: 0644]
setuptools-0.6c16dev6.egg/setuptools/command/scriptsetup.py

diff --git a/misc/build_helpers/windows/installer/.gitattributes b/misc/build_helpers/windows/installer/.gitattributes
new file mode 100644 (file)
index 0000000..33ffd98
--- /dev/null
@@ -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 (file)
index 0000000..95d7a25
--- /dev/null
@@ -0,0 +1,20 @@
+\r
+Microsoft Visual Studio Solution File, Format Version 10.00\r
+# Visual C++ Express 2008\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "installer", "installer\installer.vcproj", "{413A02C3-FBCC-42B4-9277-F1CCBDD274CA}"\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Win32 = Debug|Win32\r
+               Release|Win32 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {413A02C3-FBCC-42B4-9277-F1CCBDD274CA}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {413A02C3-FBCC-42B4-9277-F1CCBDD274CA}.Debug|Win32.Build.0 = Debug|Win32\r
+               {413A02C3-FBCC-42B4-9277-F1CCBDD274CA}.Release|Win32.ActiveCfg = Release|Win32\r
+               {413A02C3-FBCC-42B4-9277-F1CCBDD274CA}.Release|Win32.Build.0 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/misc/build_helpers/windows/installer/installer/app.rc b/misc/build_helpers/windows/installer/installer/app.rc
new file mode 100644 (file)
index 0000000..f82f4b6
--- /dev/null
@@ -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 (file)
index 0000000..f372e47
--- /dev/null
@@ -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 (file)
index 0000000..a2699a2
--- /dev/null
@@ -0,0 +1,237 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9.00"\r
+       Name="installer"\r
+       ProjectGUID="{413A02C3-FBCC-42B4-9277-F1CCBDD274CA}"\r
+       RootNamespace="installer"\r
+       Keyword="Win32Proj"\r
+       TargetFrameworkVersion="196613"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="2"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="4"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               LinkIncremental="2"\r
+                               UACExecutionLevel="2"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                               VerboseOutput="true"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                               Description="Making self-extracting executable..."\r
+                               CommandLine="cmd /c &quot;copy /y /b $(OutDir)\$(ProjectName).exe+C:\tahoe\allmydata-tahoe-1.10.0.zip $(OutDir)\$(ProjectName).exe&quot;"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="1"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="2"\r
+                               EnableIntrinsicFunctions="true"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"\r
+                               RuntimeLibrary="2"\r
+                               EnableFunctionLevelLinking="true"\r
+                               UsePrecompiledHeader="2"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               LinkIncremental="1"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath=".\installer.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\stdafx.cpp"\r
+                               >\r
+                               <FileConfiguration\r
+                                       Name="Debug|Win32"\r
+                                       >\r
+                                       <Tool\r
+                                               Name="VCCLCompilerTool"\r
+                                               UsePrecompiledHeader="1"\r
+                                       />\r
+                               </FileConfiguration>\r
+                               <FileConfiguration\r
+                                       Name="Release|Win32"\r
+                                       >\r
+                                       <Tool\r
+                                               Name="VCCLCompilerTool"\r
+                                               UsePrecompiledHeader="1"\r
+                                       />\r
+                               </FileConfiguration>\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+                       <File\r
+                               RelativePath=".\_version.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\stdafx.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\targetver.h"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+                       <File\r
+                               RelativePath=".\app.rc"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath=".\logo.ico"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/misc/build_helpers/windows/installer/installer/logo.ico b/misc/build_helpers/windows/installer/installer/logo.ico
new file mode 100644 (file)
index 0000000..8465670
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 (file)
index 0000000..6de798e
--- /dev/null
@@ -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 (executable)
index 0000000..987e0ec
--- /dev/null
@@ -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 (file)
index 0000000..c1026ab
--- /dev/null
@@ -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 (file)
index 0000000..9f8375b
--- /dev/null
@@ -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 (file)
index 0000000..6fe8eb7
--- /dev/null
@@ -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
+
index db68c07a08e91cbb848d4dd8e948f7480c9fc891..e4dd9dd0a26beb35afc29f438fbd2be5dfb4f284 100644 (file)
@@ -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)