mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Calibre Portable: Create an exe installer
This commit is contained in:
parent
2112ee3e17
commit
e5e5da661e
BIN
icons/install.ico
Normal file
BIN
icons/install.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
@ -141,7 +141,7 @@ class GoogleCode(Base):# {{{
|
||||
# The pattern to match filenames for the files being uploaded and
|
||||
# extract version information from them. Must have a named group
|
||||
# named version
|
||||
filename_pattern=r'{appname}-(?:portable-)?(?P<version>.+?)(?:-(?:i686|x86_64|32bit|64bit))?\.(?:zip|exe|msi|dmg|tar\.bz2|tar\.xz|txz|tbz2)'
|
||||
filename_pattern=r'{appname}-(?:portable-installer-)?(?P<version>.+?)(?:-(?:i686|x86_64|32bit|64bit))?\.(?:zip|exe|msi|dmg|tar\.bz2|tar\.xz|txz|tbz2)'
|
||||
|
||||
):
|
||||
self.username, self.password, = username, password
|
||||
|
4379
setup/installer/windows/XUnzip.cpp
Normal file
4379
setup/installer/windows/XUnzip.cpp
Normal file
File diff suppressed because it is too large
Load Diff
382
setup/installer/windows/XUnzip.h
Normal file
382
setup/installer/windows/XUnzip.h
Normal file
@ -0,0 +1,382 @@
|
||||
// XUnzip.h Version 1.3
|
||||
//
|
||||
// Authors: Mark Adler et al. (see below)
|
||||
//
|
||||
// Modified by: Lucian Wischik
|
||||
// lu@wischik.com
|
||||
//
|
||||
// Version 1.0 - Turned C files into just a single CPP file
|
||||
// - Made them compile cleanly as C++ files
|
||||
// - Gave them simpler APIs
|
||||
// - Added the ability to zip/unzip directly in memory without
|
||||
// any intermediate files
|
||||
//
|
||||
// Modified by: Hans Dietrich
|
||||
// hdietrich@gmail.com
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Lucian Wischik's comments:
|
||||
// --------------------------
|
||||
// THIS FILE is almost entirely based upon code by info-zip.
|
||||
// It has been modified by Lucian Wischik.
|
||||
// The original code may be found at http://www.info-zip.org
|
||||
// The original copyright text follows.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Original authors' comments:
|
||||
// ---------------------------
|
||||
// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The
|
||||
// definitive version of this document should be available at
|
||||
// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely.
|
||||
//
|
||||
// Copyright (c) 1990-2002 Info-ZIP. All rights reserved.
|
||||
//
|
||||
// For the purposes of this copyright and license, "Info-ZIP" is defined as
|
||||
// the following set of individuals:
|
||||
//
|
||||
// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois,
|
||||
// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase,
|
||||
// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz,
|
||||
// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko,
|
||||
// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs,
|
||||
// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler,
|
||||
// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White
|
||||
//
|
||||
// This software is provided "as is", without warranty of any kind, express
|
||||
// or implied. In no event shall Info-ZIP or its contributors be held liable
|
||||
// for any direct, indirect, incidental, special or consequential damages
|
||||
// arising out of the use of or inability to use this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// definition, disclaimer, and this list of conditions.
|
||||
//
|
||||
// 2. Redistributions in binary form (compiled executables) must reproduce
|
||||
// the above copyright notice, definition, disclaimer, and this list of
|
||||
// conditions in documentation and/or other materials provided with the
|
||||
// distribution. The sole exception to this condition is redistribution
|
||||
// of a standard UnZipSFX binary as part of a self-extracting archive;
|
||||
// that is permitted without inclusion of this license, as long as the
|
||||
// normal UnZipSFX banner has not been removed from the binary or disabled.
|
||||
//
|
||||
// 3. Altered versions--including, but not limited to, ports to new
|
||||
// operating systems, existing ports with new graphical interfaces, and
|
||||
// dynamic, shared, or static library versions--must be plainly marked
|
||||
// as such and must not be misrepresented as being the original source.
|
||||
// Such altered versions also must not be misrepresented as being
|
||||
// Info-ZIP releases--including, but not limited to, labeling of the
|
||||
// altered versions with the names "Info-ZIP" (or any variation thereof,
|
||||
// including, but not limited to, different capitalizations),
|
||||
// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of
|
||||
// Info-ZIP. Such altered versions are further prohibited from
|
||||
// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or
|
||||
// of the Info-ZIP URL(s).
|
||||
//
|
||||
// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip",
|
||||
// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its
|
||||
// own source and binary releases.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef XUNZIP_H
|
||||
#define XUNZIP_H
|
||||
|
||||
|
||||
#ifndef XZIP_H
|
||||
DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened
|
||||
#endif
|
||||
|
||||
typedef DWORD ZRESULT;
|
||||
// return codes from any of the zip functions. Listed later.
|
||||
|
||||
#define ZIP_HANDLE 1
|
||||
#define ZIP_FILENAME 2
|
||||
#define ZIP_MEMORY 3
|
||||
|
||||
typedef struct
|
||||
{ int index; // index of this file within the zip
|
||||
char name[MAX_PATH]; // filename within the zip
|
||||
DWORD attr; // attributes, as in GetFileAttributes.
|
||||
FILETIME atime,ctime,mtime;// access, create, modify filetimes
|
||||
long comp_size; // sizes of item, compressed and uncompressed. These
|
||||
long unc_size; // may be -1 if not yet known (e.g. being streamed in)
|
||||
} ZIPENTRY;
|
||||
|
||||
typedef struct
|
||||
{ int index; // index of this file within the zip
|
||||
TCHAR name[MAX_PATH]; // filename within the zip
|
||||
DWORD attr; // attributes, as in GetFileAttributes.
|
||||
FILETIME atime,ctime,mtime;// access, create, modify filetimes
|
||||
long comp_size; // sizes of item, compressed and uncompressed. These
|
||||
long unc_size; // may be -1 if not yet known (e.g. being streamed in)
|
||||
} ZIPENTRYW;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// OpenZip()
|
||||
//
|
||||
// Purpose: Open an existing zip archive file
|
||||
//
|
||||
// Parameters: z - archive file name if flags is ZIP_FILENAME; for other
|
||||
// uses see below
|
||||
// len - for memory (ZIP_MEMORY) should be the buffer size;
|
||||
// for other uses, should be 0
|
||||
// flags - indicates usage, see below; for files, this will be
|
||||
// ZIP_FILENAME
|
||||
//
|
||||
// Returns: HZIP - non-zero if zip archive opened ok, otherwise 0
|
||||
//
|
||||
HZIP OpenZip(void *z, unsigned int len, DWORD flags);
|
||||
// OpenZip - opens a zip file and returns a handle with which you can
|
||||
// subsequently examine its contents. You can open a zip file from:
|
||||
// from a pipe: OpenZip(hpipe_read,0, ZIP_HANDLE);
|
||||
// from a file (by handle): OpenZip(hfile,0, ZIP_HANDLE);
|
||||
// from a file (by name): OpenZip("c:\\test.zip",0, ZIP_FILENAME);
|
||||
// from a memory block: OpenZip(bufstart, buflen, ZIP_MEMORY);
|
||||
// If the file is opened through a pipe, then items may only be
|
||||
// accessed in increasing order, and an item may only be unzipped once,
|
||||
// although GetZipItem can be called immediately before and after unzipping
|
||||
// it. If it's opened i n any other way, then full random access is possible.
|
||||
// Note: pipe input is not yet implemented.
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// GetZipItem()
|
||||
//
|
||||
// Purpose: Get information about an item in an open zip archive
|
||||
//
|
||||
// Parameters: hz - handle of open zip archive
|
||||
// index - index number (0 based) of item in zip
|
||||
// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct
|
||||
// (if Unicode)
|
||||
//
|
||||
// Returns: ZRESULT - ZR_OK if success, otherwise some other value
|
||||
//
|
||||
|
||||
#ifdef _UNICODE
|
||||
#define GetZipItem GetZipItemW
|
||||
#else
|
||||
#define GetZipItem GetZipItemA
|
||||
#endif
|
||||
|
||||
ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze);
|
||||
ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze);
|
||||
// GetZipItem - call this to get information about an item in the zip.
|
||||
// If index is -1 and the file wasn't opened through a pipe,
|
||||
// then it returns information about the whole zipfile
|
||||
// (and in particular ze.index returns the number of index items).
|
||||
// Note: the item might be a directory (ze.attr & FILE_ATTRIBUTE_DIRECTORY)
|
||||
// See below for notes on what happens when you unzip such an item.
|
||||
// Note: if you are opening the zip through a pipe, then random access
|
||||
// is not possible and GetZipItem(-1) fails and you can't discover the number
|
||||
// of items except by calling GetZipItem on each one of them in turn,
|
||||
// starting at 0, until eventually the call fails. Also, in the event that
|
||||
// you are opening through a pipe and the zip was itself created into a pipe,
|
||||
// then then comp_size and sometimes unc_size as well may not be known until
|
||||
// after the item has been unzipped.
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FindZipItem()
|
||||
//
|
||||
// Purpose: Find item by name and return information about it
|
||||
//
|
||||
// Parameters: hz - handle of open zip archive
|
||||
// name - name of file to look for inside zip archive
|
||||
// ic - TRUE = case insensitive
|
||||
// index - pointer to index number returned, or -1
|
||||
// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct
|
||||
// (if Unicode)
|
||||
//
|
||||
// Returns: ZRESULT - ZR_OK if success, otherwise some other value
|
||||
//
|
||||
|
||||
#ifdef _UNICODE
|
||||
#define FindZipItem FindZipItemW
|
||||
#else
|
||||
#define FindZipItem FindZipItemA
|
||||
#endif
|
||||
|
||||
ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze);
|
||||
ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze);
|
||||
// FindZipItem - finds an item by name. ic means 'insensitive to case'.
|
||||
// It returns the index of the item, and returns information about it.
|
||||
// If nothing was found, then index is set to -1 and the function returns
|
||||
// an error code.
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// UnzipItem()
|
||||
//
|
||||
// Purpose: Find item by index and unzip it
|
||||
//
|
||||
// Parameters: hz - handle of open zip archive
|
||||
// index - index number of file to unzip
|
||||
// dst - target file name of unzipped file
|
||||
// len - for memory (ZIP_MEMORY. length of buffer;
|
||||
// otherwise 0
|
||||
// flags - indicates usage, see below; for files, this will be
|
||||
// ZIP_FILENAME
|
||||
//
|
||||
// Returns: ZRESULT - ZR_OK if success, otherwise some other value
|
||||
//
|
||||
|
||||
ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags);
|
||||
// UnzipItem - given an index to an item, unzips it. You can unzip to:
|
||||
// to a pipe: UnzipItem(hz,i, hpipe_write,0,ZIP_HANDLE);
|
||||
// to a file (by handle): UnzipItem(hz,i, hfile,0,ZIP_HANDLE);
|
||||
// to a file (by name): UnzipItem(hz,i, ze.name,0,ZIP_FILENAME);
|
||||
// to a memory block: UnzipItem(hz,i, buf,buflen,ZIP_MEMORY);
|
||||
// In the final case, if the buffer isn't large enough to hold it all,
|
||||
// then the return code indicates that more is yet to come. If it was
|
||||
// large enough, and you want to know precisely how big, GetZipItem.
|
||||
// Note: zip files are normally stored with relative pathnames. If you
|
||||
// unzip with ZIP_FILENAME a relative pathname then the item gets created
|
||||
// relative to the current directory - it first ensures that all necessary
|
||||
// subdirectories have been created. Also, the item may itself be a directory.
|
||||
// If you unzip a directory with ZIP_FILENAME, then the directory gets created.
|
||||
// If you unzip it to a handle or a memory block, then nothing gets created
|
||||
// and it emits 0 bytes.
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CloseZip()
|
||||
//
|
||||
// Purpose: Close an open zip archive
|
||||
//
|
||||
// Parameters: hz - handle to an open zip archive
|
||||
//
|
||||
// Returns: ZRESULT - ZR_OK if success, otherwise some other value
|
||||
//
|
||||
ZRESULT CloseZip(HZIP hz);
|
||||
// CloseZip - the zip handle must be closed with this function.
|
||||
|
||||
unsigned int FormatZipMessage(ZRESULT code, char *buf,unsigned int len);
|
||||
// FormatZipMessage - given an error code, formats it as a string.
|
||||
// It returns the length of the error message. If buf/len points
|
||||
// to a real buffer, then it also writes as much as possible into there.
|
||||
|
||||
|
||||
// These are the result codes:
|
||||
#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned,
|
||||
#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage.
|
||||
// The following come from general system stuff (e.g. files not openable)
|
||||
#define ZR_GENMASK 0x0000FF00
|
||||
#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle
|
||||
#define ZR_NOFILE 0x00000200 // couldn't create/open the file
|
||||
#define ZR_NOALLOC 0x00000300 // failed to allocate some resource
|
||||
#define ZR_WRITE 0x00000400 // a general error writing to the file
|
||||
#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip
|
||||
#define ZR_MORE 0x00000600 // there's still more data to be unzipped
|
||||
#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile
|
||||
#define ZR_READ 0x00000800 // a general error reading the file
|
||||
// The following come from mistakes on the part of the caller
|
||||
#define ZR_CALLERMASK 0x00FF0000
|
||||
#define ZR_ARGS 0x00010000 // general mistake with the arguments
|
||||
#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't
|
||||
#define ZR_MEMSIZE 0x00030000 // the memory size is too small
|
||||
#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function
|
||||
#define ZR_ENDED 0x00050000 // the zip creation has already been closed
|
||||
#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken
|
||||
#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped
|
||||
#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip
|
||||
// The following come from bugs within the zip library itself
|
||||
#define ZR_BUGMASK 0xFF000000
|
||||
#define ZR_NOTINITED 0x01000000 // initialisation didn't work
|
||||
#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file
|
||||
#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed
|
||||
#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// e.g.
|
||||
//
|
||||
// SetCurrentDirectory("c:\\docs\\stuff");
|
||||
// HZIP hz = OpenZip("c:\\stuff.zip",0,ZIP_FILENAME);
|
||||
// ZIPENTRY ze; GetZipItem(hz,-1,&ze); int numitems=ze.index;
|
||||
// for (int i=0; i<numitems; i++)
|
||||
// { GetZipItem(hz,i,&ze);
|
||||
// UnzipItem(hz,i,ze.name,0,ZIP_FILENAME);
|
||||
// }
|
||||
// CloseZip(hz);
|
||||
//
|
||||
//
|
||||
// HRSRC hrsrc = FindResource(hInstance,MAKEINTRESOURCE(1),RT_RCDATA);
|
||||
// HANDLE hglob = LoadResource(hInstance,hrsrc);
|
||||
// void *zipbuf=LockResource(hglob);
|
||||
// unsigned int ziplen=SizeofResource(hInstance,hrsrc);
|
||||
// HZIP hz = OpenZip(zipbuf, ziplen, ZIP_MEMORY);
|
||||
// - unzip to a membuffer -
|
||||
// ZIPENTRY ze; int i; FindZipItem(hz,"file.dat",&i,&ze);
|
||||
// char *ibuf = new char[ze.unc_size];
|
||||
// UnzipItem(hz,i, ibuf, ze.unc_size,ZIP_MEMORY);
|
||||
// delete[] buf;
|
||||
// - unzip to a fixed membuff -
|
||||
// ZIPENTRY ze; int i; FindZipItem(hz,"file.dat",&i,&ze);
|
||||
// char ibuf[1024]; ZIPRESULT zr=ZR_MORE; unsigned long totsize=0;
|
||||
// while (zr==ZR_MORE)
|
||||
// { zr = UnzipItem(hz,i, ibuf,1024,ZIP_MEMORY);
|
||||
// unsigned long bufsize=1024; if (zr==ZR_OK) bufsize=ze.unc_size-totsize;
|
||||
// totsize+=bufsize;
|
||||
// }
|
||||
// - unzip to a pipe -
|
||||
// HANDLE hthread=CreateWavReaderThread(&hread,&hwrite);
|
||||
// FindZipItem(hz,"sound.wav",&i,&ze);
|
||||
// UnzipItem(hz,i, hwrite,0,ZIP_HANDLE);
|
||||
// CloseHandle(hwrite);
|
||||
// WaitForSingleObject(hthread,INFINITE);
|
||||
// CloseHandle(hread); CloseHandle(hthread);
|
||||
// - finished -
|
||||
// CloseZip(hz);
|
||||
// // note: no need to free resources obtained through Find/Load/LockResource
|
||||
//
|
||||
//
|
||||
// SetCurrentDirectory("c:\\docs\\pipedzipstuff");
|
||||
// HANDLE hread,hwrite; CreatePipe(&hread,&hwrite);
|
||||
// CreateZipWriterThread(hwrite);
|
||||
// HZIP hz = OpenZip(hread,0,ZIP_HANDLE);
|
||||
// for (int i=0; ; i++)
|
||||
// { ZIPENTRY ze; ZRESULT res = GetZipItem(hz,i,&ze);
|
||||
// if (res!=ZE_OK) break; // no more
|
||||
// UnzipItem(hz,i, ze.name,0,ZIP_FILENAME);
|
||||
// }
|
||||
// CloseZip(hz);
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
// Now we indulge in a little skullduggery so that the code works whether
|
||||
// the user has included just zip or both zip and unzip.
|
||||
// Idea: if header files for both zip and unzip are present, then presumably
|
||||
// the cpp files for zip and unzip are both present, so we will call
|
||||
// one or the other of them based on a dynamic choice. If the header file
|
||||
// for only one is present, then we will bind to that particular one.
|
||||
HZIP OpenZipU(void *z,unsigned int len,DWORD flags);
|
||||
ZRESULT CloseZipU(HZIP hz);
|
||||
unsigned int FormatZipMessageU(ZRESULT code, char *buf,unsigned int len);
|
||||
bool IsZipHandleU(HZIP hz);
|
||||
#define OpenZip OpenZipU
|
||||
|
||||
#ifdef XZIP_H
|
||||
#undef CloseZip
|
||||
#define CloseZip(hz) (IsZipHandleU(hz)?CloseZipU(hz):CloseZipZ(hz))
|
||||
#else
|
||||
#define CloseZip CloseZipU
|
||||
#define FormatZipMessage FormatZipMessageU
|
||||
#endif
|
||||
|
||||
|
||||
#endif //XUNZIP_H
|
@ -37,7 +37,7 @@ class Win32(VMInstaller):
|
||||
SHUTDOWN_CMD = ['shutdown.exe', '-s', '-f', '-t', '0']
|
||||
|
||||
def sign_msi(self):
|
||||
print ('Signing .msi ...')
|
||||
print ('Signing installers ...')
|
||||
raw = open(self.VM).read()
|
||||
vmx = re.search(r'''launch_vmware\(['"](.+?)['"]''', raw).group(1)
|
||||
subprocess.check_call(['vmrun', '-T', 'ws', '-gu', 'kovid', '-gp',
|
||||
@ -56,7 +56,7 @@ class Win32(VMInstaller):
|
||||
self.warn('Failed to freeze')
|
||||
raise SystemExit(1)
|
||||
|
||||
installer = 'dist/%s-portable-%s.zip'%(__appname__, __version__)
|
||||
installer = 'dist/%s-portable-installer-%s.exe'%(__appname__, __version__)
|
||||
subprocess.check_call(('scp',
|
||||
'xp_build:build/%s/%s'%(__appname__, installer), 'dist'))
|
||||
if not os.path.exists(installer):
|
||||
|
@ -23,6 +23,7 @@ SW = r'C:\cygwin\home\kovid\sw'
|
||||
IMAGEMAGICK = os.path.join(SW, 'build', 'ImageMagick-6.7.6',
|
||||
'VisualMagick', 'bin')
|
||||
CRT = r'C:\Microsoft.VC90.CRT'
|
||||
LZMA = r'Q:\easylzma\build\easylzma-0.0.8'
|
||||
|
||||
VERSION = re.sub('[a-z]\d+', '', __version__)
|
||||
WINVER = VERSION+'.0'
|
||||
@ -89,6 +90,7 @@ class Win32Freeze(Command, WixMixIn):
|
||||
self.remove_CRT_from_manifests()
|
||||
self.create_installer()
|
||||
self.build_portable()
|
||||
self.build_portable_installer()
|
||||
|
||||
def remove_CRT_from_manifests(self):
|
||||
'''
|
||||
@ -311,7 +313,8 @@ class Win32Freeze(Command, WixMixIn):
|
||||
subprocess.check_call([r'C:\Program Files\7-Zip\7z.exe', 'a', '-r',
|
||||
'-scsUTF-8', '-sfx', 'winfrozen', 'winfrozen'], cwd=self.base)
|
||||
|
||||
def embed_resources(self, module, desc=None):
|
||||
def embed_resources(self, module, desc=None, extra_data=None,
|
||||
product_description=None):
|
||||
icon_base = self.j(self.src_root, 'icons')
|
||||
icon_map = {'calibre':'library', 'ebook-viewer':'viewer',
|
||||
'lrfviewer':'viewer', 'calibre-portable':'library'}
|
||||
@ -320,6 +323,8 @@ class Win32Freeze(Command, WixMixIn):
|
||||
bname = self.b(module)
|
||||
internal_name = os.path.splitext(bname)[0]
|
||||
icon = icon_map.get(internal_name, 'command-prompt')
|
||||
if internal_name.startswith('calibre-portable-'):
|
||||
icon = 'install'
|
||||
icon = self.j(icon_base, icon+'.ico')
|
||||
if desc is None:
|
||||
defdesc = 'A dynamic link library' if file_type == 'DLL' else \
|
||||
@ -327,6 +332,8 @@ class Win32Freeze(Command, WixMixIn):
|
||||
desc = DESCRIPTIONS.get(internal_name, defdesc)
|
||||
license = 'GNU GPL v3.0'
|
||||
def e(val): return val.replace('"', r'\"')
|
||||
if product_description is None:
|
||||
product_description = __appname__ + ' - E-book management'
|
||||
rc = template.format(
|
||||
icon=icon,
|
||||
file_type=e(file_type),
|
||||
@ -338,11 +345,13 @@ class Win32Freeze(Command, WixMixIn):
|
||||
product_version=e(WINVER.replace('.', ',')),
|
||||
product_version_str=e(__version__),
|
||||
product_name=e(__appname__),
|
||||
product_description=e(__appname__+' - E-book management'),
|
||||
product_description=e(product_description),
|
||||
legal_copyright=e(license),
|
||||
legal_trademarks=e(__appname__ + \
|
||||
' is a registered U.S. trademark number 3,666,525')
|
||||
)
|
||||
if extra_data:
|
||||
rc += '\nextra extra "%s"'%extra_data
|
||||
tdir = self.obj_dir
|
||||
rcf = self.j(tdir, bname+'.rc')
|
||||
with open(rcf, 'wb') as f:
|
||||
@ -371,6 +380,45 @@ class Win32Freeze(Command, WixMixIn):
|
||||
self.info(p.stderr.read())
|
||||
sys.exit(1)
|
||||
|
||||
def build_portable_installer(self):
|
||||
base = self.portable_base
|
||||
zf = self.a(self.j('dist', 'calibre-portable-%s.zip.lz'%VERSION))
|
||||
usz = os.path.getsize(zf)
|
||||
def cc(src, obj):
|
||||
cflags = '/c /EHsc /MT /W4 /Ox /nologo /D_UNICODE /DUNICODE'.split()
|
||||
cflags.append(r'/I%s\include'%LZMA)
|
||||
cflags.append('/DUNCOMPRESSED_SIZE=%d'%usz)
|
||||
if self.newer(obj, [src]):
|
||||
self.info('Compiling', obj)
|
||||
cmd = [msvc.cc] + cflags + ['/Fo'+obj, src]
|
||||
self.run_builder(cmd)
|
||||
|
||||
src = self.j(self.src_root, 'setup', 'installer', 'windows',
|
||||
'portable-installer.cpp')
|
||||
obj = self.j(self.obj_dir, self.b(src)+'.obj')
|
||||
xsrc = self.j(self.src_root, 'setup', 'installer', 'windows',
|
||||
'XUnzip.cpp')
|
||||
xobj = self.j(self.obj_dir, self.b(xsrc)+'.obj')
|
||||
cc(src, obj)
|
||||
cc(xsrc, xobj)
|
||||
|
||||
exe = self.j('dist', 'calibre-portable-installer-%s.exe'%VERSION)
|
||||
if self.newer(exe, [obj, xobj]):
|
||||
self.info('Linking', exe)
|
||||
cmd = [msvc.linker] + ['/INCREMENTAL:NO', '/MACHINE:X86',
|
||||
'/LIBPATH:'+self.obj_dir, '/SUBSYSTEM:WINDOWS',
|
||||
'/LIBPATH:'+(LZMA+r'\lib\Release'),
|
||||
'/RELEASE',
|
||||
'/ENTRY:wWinMainCRTStartup',
|
||||
'/OUT:'+exe, self.embed_resources(exe,
|
||||
desc='Calibre Portable Installer', extra_data=zf,
|
||||
product_description='Calibre Portable Installer'),
|
||||
xobj, obj, 'User32.lib', 'Shell32.lib', 'easylzma_s.lib',
|
||||
'Ole32.lib', 'Shlwapi.lib', 'Kernel32.lib']
|
||||
self.run_builder(cmd)
|
||||
|
||||
os.remove(zf)
|
||||
|
||||
def build_portable(self):
|
||||
base = self.portable_base
|
||||
if os.path.exists(base):
|
||||
@ -403,9 +451,12 @@ class Win32Freeze(Command, WixMixIn):
|
||||
os.mkdir(self.j(base, 'Calibre Settings'))
|
||||
|
||||
name = '%s-portable-%s.zip'%(__appname__, __version__)
|
||||
with zipfile.ZipFile(self.j('dist', name), 'w', zipfile.ZIP_DEFLATED) as zf:
|
||||
name = self.j('dist', name)
|
||||
with zipfile.ZipFile(name, 'w', zipfile.ZIP_STORED) as zf:
|
||||
self.add_dir_to_zip(zf, base, 'Calibre Portable')
|
||||
|
||||
subprocess.check_call([LZMA + r'\bin\elzma.exe', '-9', '--lzip', name])
|
||||
|
||||
def add_dir_to_zip(self, zf, path, prefix=''):
|
||||
'''
|
||||
Add a directory recursively to the zip file with an optional prefix.
|
||||
|
@ -370,6 +370,14 @@ Run
|
||||
Python setup.py build
|
||||
cp -r build/lib.win32-*/* /cygdrive/c/Python27/Lib/site-packages/
|
||||
|
||||
easylzma
|
||||
----------
|
||||
|
||||
Get it from http://lloyd.github.com/easylzma/ (use the trunk version)
|
||||
|
||||
Run cmake and build the Visual Studio solution (generates CLI tools and dll and
|
||||
static lib automatically)
|
||||
|
||||
calibre
|
||||
---------
|
||||
|
||||
|
547
setup/installer/windows/portable-installer.cpp
Normal file
547
setup/installer/windows/portable-installer.cpp
Normal file
@ -0,0 +1,547 @@
|
||||
#ifndef UNICODE
|
||||
#define UNICODE
|
||||
#endif
|
||||
|
||||
#ifndef _UNICODE
|
||||
#define _UNICODE
|
||||
#endif
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Shlobj.h>
|
||||
#include <Shlwapi.h>
|
||||
#include <Shellapi.h>
|
||||
#include <wchar.h>
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <easylzma/decompress.h>
|
||||
#include "XUnzip.h"
|
||||
|
||||
#define BUFSIZE 4096
|
||||
|
||||
// Error handling {{{
|
||||
|
||||
static void show_error(LPCWSTR msg) {
|
||||
MessageBeep(MB_ICONERROR);
|
||||
MessageBox(NULL, msg, L"Error", MB_OK|MB_ICONERROR);
|
||||
}
|
||||
|
||||
static void show_detailed_error(LPCWSTR preamble, LPCWSTR msg, int code) {
|
||||
LPWSTR buf;
|
||||
buf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR)*
|
||||
(wcslen(msg) + wcslen(preamble) + 80));
|
||||
|
||||
_snwprintf_s(buf,
|
||||
LocalSize(buf) / sizeof(WCHAR), _TRUNCATE,
|
||||
L"%s\r\n %s (Error Code: %d)\r\n",
|
||||
preamble, msg, code);
|
||||
|
||||
show_error(buf);
|
||||
LocalFree(buf);
|
||||
}
|
||||
|
||||
static void show_zip_error(LPCWSTR preamble, LPCWSTR msg, ZRESULT code) {
|
||||
LPWSTR buf;
|
||||
buf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR)*
|
||||
(wcslen(preamble) + wcslen(msg) + 80));
|
||||
|
||||
_snwprintf_s(buf,
|
||||
LocalSize(buf) / sizeof(WCHAR), _TRUNCATE,
|
||||
L"%s\r\n %s %s (Error Code: %X)\r\n",
|
||||
preamble, msg, code);
|
||||
|
||||
show_error(buf);
|
||||
LocalFree(buf);
|
||||
}
|
||||
|
||||
static void show_last_error_crt(LPCWSTR preamble) {
|
||||
WCHAR buf[BUFSIZE];
|
||||
int err = 0;
|
||||
|
||||
_get_errno(&err);
|
||||
_wcserror_s(buf, BUFSIZE, err);
|
||||
show_detailed_error(preamble, buf, err);
|
||||
}
|
||||
|
||||
static void show_last_error(LPCWSTR preamble) {
|
||||
WCHAR *msg = NULL;
|
||||
DWORD dw = GetLastError();
|
||||
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
dw,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPWSTR)&msg,
|
||||
0, NULL );
|
||||
|
||||
show_detailed_error(preamble, msg, (int)dw);
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// Load, decompress and extract data {{{
|
||||
|
||||
static BOOL load_data(LPVOID *data, DWORD *sz) {
|
||||
HRSRC rsrc;
|
||||
HGLOBAL h;
|
||||
|
||||
rsrc = FindResourceW(NULL, L"extra", L"extra");
|
||||
if (rsrc == NULL) { show_last_error(L"Failed to find portable data in exe"); return false; }
|
||||
|
||||
h = LoadResource(NULL, rsrc);
|
||||
if (h == NULL) { show_last_error(L"Failed to load portable data from exe"); return false; }
|
||||
|
||||
*data = LockResource(h);
|
||||
if (*data == NULL) { show_last_error(L"Failed to lock portable data in exe"); return false; }
|
||||
|
||||
*sz = SizeofResource(NULL, rsrc);
|
||||
if (sz == 0) { show_last_error(L"Failed to get size of portable data in exe"); return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static BOOL unzip(HZIP zipf, int nitems, IProgressDialog *pd) {
|
||||
int i = 0;
|
||||
ZRESULT res;
|
||||
ZIPENTRYW ze;
|
||||
|
||||
for (i = 0; i < nitems; i++) {
|
||||
res = GetZipItem(zipf, i, &ze);
|
||||
if (res != ZR_OK) { show_zip_error(L"Failed to get zip item", L"", res); return false;}
|
||||
|
||||
res = UnzipItem(zipf, i, ze.name, 0, ZIP_FILENAME);
|
||||
if (res != ZR_OK) { show_zip_error(L"Failed to extract zip item:", ze.name, res); return false;}
|
||||
|
||||
pd->SetLine(2, ze.name, true, NULL);
|
||||
pd->SetProgress(i, nitems);
|
||||
}
|
||||
|
||||
CloseZip(zipf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static HANDLE temp_file(LPWSTR name) {
|
||||
UINT res;
|
||||
HANDLE h;
|
||||
|
||||
res = GetTempFileNameW(L".", L"portable_data", 0, name);
|
||||
|
||||
if (res == 0) { show_last_error(L"Failed to create temporary file to decompress portable data"); return INVALID_HANDLE_VALUE; }
|
||||
|
||||
h = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) { show_last_error(L"Failed to open temp file t decomress portable data"); }
|
||||
return h;
|
||||
|
||||
}
|
||||
|
||||
struct DataStream
|
||||
{
|
||||
const unsigned char *in_data;
|
||||
size_t in_len;
|
||||
|
||||
HANDLE out;
|
||||
IProgressDialog *pd;
|
||||
};
|
||||
|
||||
static int
|
||||
input_callback(void *ctx, void *buf, size_t * size)
|
||||
{
|
||||
size_t rd = 0;
|
||||
struct DataStream * ds = (struct DataStream *) ctx;
|
||||
|
||||
rd = (ds->in_len < *size) ? ds->in_len : *size;
|
||||
|
||||
if (rd > 0) {
|
||||
memcpy(buf, (void*) ds->in_data, rd);
|
||||
ds->in_data += rd;
|
||||
ds->in_len -= rd;
|
||||
}
|
||||
|
||||
*size = rd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
output_callback(void *ctx, const void *buf, size_t size)
|
||||
{
|
||||
struct DataStream * ds = (struct DataStream *) ctx;
|
||||
DWORD written = 0;
|
||||
|
||||
if (size > 0) {
|
||||
if (!WriteFile(ds->out, buf, size, &written, NULL)) {
|
||||
show_last_error(L"Failed to write uncompressed data to temp file");
|
||||
return 0;
|
||||
}
|
||||
written = SetFilePointer(ds->out, 0, NULL, FILE_CURRENT);
|
||||
ds->pd->SetProgress(written, UNCOMPRESSED_SIZE);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static BOOL decompress(LPVOID src, DWORD src_sz, HANDLE out, IProgressDialog *pd) {
|
||||
elzma_decompress_handle h;
|
||||
struct DataStream ds;
|
||||
int rc;
|
||||
|
||||
h = elzma_decompress_alloc();
|
||||
|
||||
if (h == NULL) { show_error(L"Out of memory"); return false; }
|
||||
|
||||
ds.in_data = (unsigned char*)src;
|
||||
ds.in_len = src_sz;
|
||||
ds.out = out;
|
||||
ds.pd = pd;
|
||||
|
||||
rc = elzma_decompress_run(h, input_callback, (void *) &ds, output_callback,
|
||||
(void *) &ds, ELZMA_lzip);
|
||||
|
||||
if (rc != ELZMA_E_OK) {
|
||||
elzma_decompress_free(&h);
|
||||
show_zip_error(L"Failed to decompress portable data", L"", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
elzma_decompress_free(&h);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static BOOL extract(LPVOID cdata, DWORD csz) {
|
||||
HANDLE h;
|
||||
WCHAR tempnam[MAX_PATH+1] = {0};
|
||||
BOOL ret = true;
|
||||
HZIP zipf;
|
||||
ZIPENTRYW ze;
|
||||
ZRESULT res;
|
||||
int nitems;
|
||||
HRESULT hr;
|
||||
IProgressDialog *pd = NULL;
|
||||
|
||||
hr = CoCreateInstance(CLSID_ProgressDialog, NULL,
|
||||
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pd));
|
||||
|
||||
if (FAILED(hr)) { show_error(L"Failed to create progress dialog"); return false; }
|
||||
pd->SetTitle(L"Extracting Calibre Portable");
|
||||
pd->SetLine(1, L"Decompressing data...", true, NULL);
|
||||
|
||||
h = temp_file(tempnam);
|
||||
if (h == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
pd->StartProgressDialog(NULL, NULL, PROGDLG_NORMAL | PROGDLG_AUTOTIME | PROGDLG_NOCANCEL, NULL);
|
||||
if (!decompress(cdata, csz, h, pd)) { ret = false; goto end; }
|
||||
SetFilePointer(h, 0, NULL, FILE_BEGIN);
|
||||
zipf = OpenZip(h, 0, ZIP_HANDLE);
|
||||
if (zipf == 0) { show_last_error(L"Failed to open zipped portable data"); ret = false; goto end; }
|
||||
|
||||
res = GetZipItem(zipf, -1, &ze);
|
||||
if (res != ZR_OK) { show_zip_error(L"Failed to get count of items in portable data", L"", res); ret = false; goto end;}
|
||||
nitems = ze.index;
|
||||
|
||||
pd->SetLine(1, L"Copying files...", true, NULL);
|
||||
if (!unzip(zipf, nitems, pd)) { ret = false; goto end; }
|
||||
end:
|
||||
pd->StopProgressDialog();
|
||||
pd->Release();
|
||||
CloseHandle(h);
|
||||
DeleteFile(tempnam);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// Find calibre portable directory and install/upgrade into it {{{
|
||||
|
||||
static BOOL directory_exists( LPCWSTR path )
|
||||
{
|
||||
if( _waccess_s( path, 0 ) == 0 )
|
||||
{
|
||||
struct _stat status;
|
||||
_wstat( path, &status );
|
||||
return (status.st_mode & S_IFDIR) != 0;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL file_exists( LPCWSTR path )
|
||||
{
|
||||
if( _waccess_s( path, 0 ) == 0 )
|
||||
{
|
||||
struct _stat status;
|
||||
_wstat( path, &status );
|
||||
return (status.st_mode & S_IFREG) != 0;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static LPWSTR get_directory_from_user() {
|
||||
WCHAR name[MAX_PATH+1] = {0};
|
||||
LPWSTR path = NULL;
|
||||
PIDLIST_ABSOLUTE ret;
|
||||
|
||||
path = (LPWSTR)calloc(2*MAX_PATH, sizeof(WCHAR));
|
||||
if (path == NULL) { show_error(L"Out of memory"); return NULL; }
|
||||
|
||||
int image = 0;
|
||||
BROWSEINFO bi = { NULL, NULL, name,
|
||||
L"Select the folder where you want to install or update Calibre Portable",
|
||||
BIF_RETURNONLYFSDIRS | BIF_DONTGOBELOWDOMAIN | BIF_USENEWUI,
|
||||
NULL, NULL, image };
|
||||
|
||||
ret = SHBrowseForFolder(&bi);
|
||||
if (ret == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!SHGetPathFromIDList(ret, path)) {
|
||||
show_detailed_error(L"The selected folder is not valid: ", name, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return path;
|
||||
|
||||
}
|
||||
|
||||
static bool is_dots(LPCWSTR name) {
|
||||
return wcscmp(name, L".") == 0 || wcscmp(name, L"..") == 0;
|
||||
}
|
||||
|
||||
static bool rmtree(LPCWSTR path) {
|
||||
SHFILEOPSTRUCTW op;
|
||||
WCHAR buf[4*MAX_PATH + 2] = {0};
|
||||
|
||||
if (GetFullPathName(path, 4*MAX_PATH, buf, NULL) == 0) return false;
|
||||
|
||||
op.hwnd = NULL;
|
||||
op.wFunc = FO_DELETE;
|
||||
op.pFrom = buf;
|
||||
op.pTo = NULL;
|
||||
op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMMKDIR;
|
||||
op.fAnyOperationsAborted = false;
|
||||
op.hNameMappings = NULL;
|
||||
op.lpszProgressTitle = NULL;
|
||||
|
||||
return SHFileOperationW(&op) == 0;
|
||||
}
|
||||
|
||||
static BOOL find_portable_dir(LPCWSTR base, LPWSTR *result, BOOL *existing) {
|
||||
WCHAR buf[4*MAX_PATH] = {0};
|
||||
|
||||
_snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\calibre-portable.exe", base);
|
||||
*existing = true;
|
||||
|
||||
if (file_exists(buf)) {
|
||||
*result = _wcsdup(base);
|
||||
if (*result == NULL) { show_error(L"Out of memory"); return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
WIN32_FIND_DATA fdFile;
|
||||
HANDLE hFind = NULL;
|
||||
_snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\*", base);
|
||||
|
||||
if((hFind = FindFirstFileEx(buf, FindExInfoStandard, &fdFile, FindExSearchLimitToDirectories, NULL, 0)) != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if(is_dots(fdFile.cFileName)) continue;
|
||||
|
||||
if(fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
_snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\%s\\calibre-portable.exe", base, fdFile.cFileName);
|
||||
if (file_exists(buf)) {
|
||||
*result = _wcsdup(buf);
|
||||
if (*result == NULL) { show_error(L"Out of memory"); return false; }
|
||||
PathRemoveFileSpec(*result);
|
||||
FindClose(hFind);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while(FindNextFile(hFind, &fdFile));
|
||||
FindClose(hFind);
|
||||
}
|
||||
|
||||
*existing = false;
|
||||
_snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\Calibre Portable", base);
|
||||
if (!CreateDirectory(buf, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
show_last_error(L"Failed to create Calibre Portable folder");
|
||||
return false;
|
||||
}
|
||||
*result = _wcsdup(buf);
|
||||
if (*result == NULL) { show_error(L"Out of memory"); return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static LPWSTR make_unpack_dir() {
|
||||
WCHAR buf[4*MAX_PATH] = {0};
|
||||
LPWSTR ans = NULL;
|
||||
|
||||
if (directory_exists(L"_unpack_calibre_portable"))
|
||||
rmtree(L"_unpack_calibre_portable");
|
||||
|
||||
if (!CreateDirectory(L"_unpack_calibre_portable", NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
show_last_error(L"Failed to create temporary folder to unpack into");
|
||||
return ans;
|
||||
}
|
||||
|
||||
if (!GetFullPathName(L"_unpack_calibre_portable", 4*MAX_PATH, buf, NULL)) {
|
||||
show_last_error(L"Failed to resolve path");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ans = _wcsdup(buf);
|
||||
if (ans == NULL) show_error(L"Out of memory");
|
||||
return ans;
|
||||
|
||||
}
|
||||
|
||||
static BOOL move_program() {
|
||||
if (MoveFileEx(L"Calibre Portable\\calibre-portable.exe",
|
||||
L"..\\calibre-portable.exe", MOVEFILE_REPLACE_EXISTING) == 0) {
|
||||
show_last_error(L"Failed to move calibre-portable.exe, make sure calibre is not running");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (directory_exists(L"..\\Calibre")) {
|
||||
if (!rmtree(L"..\\Calibre")) {
|
||||
show_error(L"Failed to delete the Calibre program folder. Make sure calibre is not running.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (MoveFileEx(L"Calibre Portable\\Calibre", L"..\\Calibre", 0) == 0) {
|
||||
show_last_error(L"Failed to move calibre program folder");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!directory_exists(L"..\\Calibre Library")) {
|
||||
MoveFileEx(L"Calibre Portable\\Calibre Library", L"..\\Calibre Library", 0);
|
||||
}
|
||||
|
||||
if (!directory_exists(L"..\\Calibre Settings")) {
|
||||
MoveFileEx(L"Calibre Portable\\Calibre Settings", L"..\\Calibre Settings", 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// }}}
|
||||
|
||||
static void launch_calibre() {
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
ZeroMemory( &si, sizeof(si) );
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory( &pi, sizeof(pi) );
|
||||
|
||||
|
||||
if (CreateProcess(_wcsdup(L"calibre-portable.exe"), NULL,
|
||||
NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP,
|
||||
NULL, NULL, &si, &pi)
|
||||
== 0) {
|
||||
show_last_error(L"Failed to launch calibre portable");
|
||||
}
|
||||
|
||||
// Close process and thread handles.
|
||||
CloseHandle( pi.hProcess );
|
||||
CloseHandle( pi.hThread );
|
||||
|
||||
}
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
||||
{
|
||||
|
||||
LPVOID cdata = NULL;
|
||||
DWORD csz = 0;
|
||||
int ret = 0, argc;
|
||||
HRESULT hr;
|
||||
LPWSTR tgt = NULL, dest = NULL, *argv, unpack_dir = NULL;
|
||||
BOOL existing = false, launch = false;
|
||||
WCHAR buf[4*MAX_PATH] = {0}, mb_msg[4*MAX_PATH] = {0}, fdest[4*MAX_PATH] = {0};
|
||||
|
||||
if (!load_data(&cdata, &csz)) return 0;
|
||||
|
||||
hr = CoInitialize(NULL);
|
||||
if (FAILED(hr)) { show_error(L"Failed to initialize COM"); return 0; }
|
||||
|
||||
// Get the target directory for installation
|
||||
argv = CommandLineToArgvW(GetCommandLine(), &argc);
|
||||
if (argv == NULL) { show_last_error(L"Failed to get command line"); return 0; }
|
||||
if (argc > 1) {
|
||||
tgt = argv[1];
|
||||
} else {
|
||||
tgt = get_directory_from_user();
|
||||
if (tgt == NULL) goto end;
|
||||
}
|
||||
|
||||
if (!directory_exists(tgt)) {
|
||||
show_detailed_error(L"The specified directory does not exist: ",
|
||||
tgt, 1);
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Ensure the path to Calibre Portable is not too long
|
||||
do {
|
||||
if (!find_portable_dir(tgt, &dest, &existing)) goto end;
|
||||
|
||||
if (GetFullPathName(dest, MAX_PATH*4, fdest, NULL) == 0) {
|
||||
show_last_error(L"Failed to resolve target folder");
|
||||
goto end;
|
||||
}
|
||||
free(dest); dest = NULL;
|
||||
|
||||
if (wcslen(fdest) > 58) {
|
||||
_snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE,
|
||||
L"Path to Calibre Portable (%s) too long. Must be less than 59 characters.", fdest);
|
||||
if (!existing) RemoveDirectory(fdest);
|
||||
show_error(buf);
|
||||
tgt = get_directory_from_user();
|
||||
if (tgt == NULL) goto end;
|
||||
}
|
||||
} while (wcslen(fdest) > 58);
|
||||
|
||||
// Confirm the user wants to upgrade
|
||||
if (existing) {
|
||||
_snwprintf_s(mb_msg, 4*MAX_PATH, _TRUNCATE,
|
||||
L"An existing install of Calibre Portable was found at %s. Do you want to upgrade it?",
|
||||
fdest);
|
||||
if (MessageBox(NULL, mb_msg,
|
||||
L"Upgrade Calibre Portable?", MB_ICONEXCLAMATION | MB_YESNO | MB_TOPMOST) != IDYES)
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Make a temp dir to unpack into
|
||||
if (!SetCurrentDirectoryW(fdest)) { show_detailed_error(L"Failed to change to unzip directory: ", fdest, 0); goto end; }
|
||||
|
||||
if ( (unpack_dir = make_unpack_dir()) == NULL ) goto end;
|
||||
if (!SetCurrentDirectoryW(unpack_dir)) { show_detailed_error(L"Failed to change to unpack directory: ", fdest, 0); goto end; }
|
||||
|
||||
// Extract files
|
||||
if (!extract(cdata, csz)) goto end;
|
||||
|
||||
// Move files from temp dir to the install dir
|
||||
if (!move_program()) goto end;
|
||||
|
||||
SetCurrentDirectoryW(L"..");
|
||||
|
||||
_snwprintf_s(mb_msg, 4*MAX_PATH, _TRUNCATE,
|
||||
L"Calibre Portable successfully installed to %s. Launch calibre?",
|
||||
fdest);
|
||||
launch = MessageBox(NULL, mb_msg,
|
||||
L"Success", MB_ICONINFORMATION | MB_YESNO | MB_TOPMOST) == IDYES;
|
||||
|
||||
end:
|
||||
if (unpack_dir != NULL) { rmtree(unpack_dir); free(unpack_dir); }
|
||||
CoUninitialize();
|
||||
if (launch) launch_calibre();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ def installers():
|
||||
installers = list(map(installer_name, ('dmg', 'msi', 'tar.bz2')))
|
||||
installers.append(installer_name('tar.bz2', is64bit=True))
|
||||
installers.insert(0, 'dist/%s-%s.tar.xz'%(__appname__, __version__))
|
||||
installers.append('dist/%s-portable-%s.zip'%(__appname__, __version__))
|
||||
installers.append('dist/%s-portable-installer-%s.exe'%(__appname__, __version__))
|
||||
return installers
|
||||
|
||||
def installer_description(fname):
|
||||
|
Loading…
x
Reference in New Issue
Block a user