mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Build a calibre portable installer in addition to the .msi
This commit is contained in:
parent
64afab7f5e
commit
fbd282480c
@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, shutil, subprocess
|
||||
|
||||
from setup import Command, __appname__
|
||||
from setup import Command, __appname__, __version__
|
||||
from setup.installer import VMInstaller
|
||||
|
||||
class Win(Command):
|
||||
@ -43,4 +43,11 @@ class Win32(VMInstaller):
|
||||
self.warn('Failed to freeze')
|
||||
raise SystemExit(1)
|
||||
|
||||
installer = 'dist/%s-portable-%s.zip'%(__appname__, __version__)
|
||||
subprocess.check_call(('scp',
|
||||
'xp_build:build/%s/%s'%(__appname__, installer), 'dist'))
|
||||
if not os.path.exists(installer):
|
||||
self.warn('Failed to get portable installer')
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
|
@ -50,7 +50,7 @@ def walk(dir):
|
||||
|
||||
class Win32Freeze(Command, WixMixIn):
|
||||
|
||||
description = 'Free windows calibre installation'
|
||||
description = 'Freeze windows calibre installation'
|
||||
|
||||
def add_options(self, parser):
|
||||
parser.add_option('--no-ice', default=False, action='store_true',
|
||||
@ -74,6 +74,8 @@ class Win32Freeze(Command, WixMixIn):
|
||||
self.pylib = self.j(self.base, 'pylib.zip')
|
||||
self.dll_dir = self.j(self.base, 'DLLs')
|
||||
self.plugins_dir = os.path.join(self.base, 'plugins2')
|
||||
self.portable_base = self.j(self.d(self.base), 'Calibre Portable')
|
||||
self.obj_dir = self.j(self.src_root, 'build', 'launcher')
|
||||
|
||||
self.initbase()
|
||||
self.build_launchers()
|
||||
@ -84,6 +86,7 @@ class Win32Freeze(Command, WixMixIn):
|
||||
self.archive_lib_dir()
|
||||
self.remove_CRT_from_manifests()
|
||||
self.create_installer()
|
||||
self.build_portable()
|
||||
|
||||
def remove_CRT_from_manifests(self):
|
||||
'''
|
||||
@ -287,7 +290,7 @@ class Win32Freeze(Command, WixMixIn):
|
||||
def embed_resources(self, module, desc=None):
|
||||
icon_base = self.j(self.src_root, 'icons')
|
||||
icon_map = {'calibre':'library', 'ebook-viewer':'viewer',
|
||||
'lrfviewer':'viewer'}
|
||||
'lrfviewer':'viewer', 'calibre-portable':'library'}
|
||||
file_type = 'DLL' if module.endswith('.dll') else 'APP'
|
||||
template = open(self.rc_template, 'rb').read()
|
||||
bname = self.b(module)
|
||||
@ -344,8 +347,62 @@ class Win32Freeze(Command, WixMixIn):
|
||||
self.info(p.stderr.read())
|
||||
sys.exit(1)
|
||||
|
||||
def build_portable(self):
|
||||
base = self.portable_base
|
||||
if os.path.exists(base):
|
||||
shutil.rmtree(base)
|
||||
os.makedirs(base)
|
||||
src = self.j(self.src_root, 'setup', 'installer', 'windows',
|
||||
'portable.c')
|
||||
obj = self.j(self.obj_dir, self.b(src)+'.obj')
|
||||
cflags = '/c /EHsc /MT /W3 /Ox /nologo /D_UNICODE'.split()
|
||||
|
||||
if self.newer(obj, [src]):
|
||||
self.info('Compiling', obj)
|
||||
cmd = [msvc.cc] + cflags + ['/Fo'+obj, '/Tc'+src]
|
||||
self.run_builder(cmd)
|
||||
|
||||
exe = self.j(base, 'calibre-portable.exe')
|
||||
if self.newer(exe, [obj]):
|
||||
self.info('Linking', exe)
|
||||
cmd = [msvc.linker] + ['/INCREMENTAL:NO', '/MACHINE:X86',
|
||||
'/LIBPATH:'+self.obj_dir, '/SUBSYSTEM:WINDOWS',
|
||||
'/RELEASE',
|
||||
'/OUT:'+exe, self.embed_resources(exe),
|
||||
obj, 'User32.lib']
|
||||
self.run_builder(cmd)
|
||||
|
||||
self.info('Creating portable installer')
|
||||
shutil.copytree(self.base, self.j(base, 'Calibre'))
|
||||
os.mkdir(self.j(base, 'Calibre Library'))
|
||||
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:
|
||||
self.add_dir_to_zip(zf, base)
|
||||
|
||||
def add_dir_to_zip(self, zf, path, prefix=''):
|
||||
'''
|
||||
Add a directory recursively to the zip file with an optional prefix.
|
||||
'''
|
||||
if prefix:
|
||||
zi = zipfile.ZipInfo(prefix+'/')
|
||||
zi.external_attr = 0700 << 16
|
||||
zf.writestr(zi, '')
|
||||
cwd = os.path.abspath(os.getcwd())
|
||||
try:
|
||||
os.chdir(path)
|
||||
fp = (prefix + ('/' if prefix else '')).replace('//', '/')
|
||||
for f in os.listdir('.'):
|
||||
arcname = fp + f
|
||||
if os.path.isdir(f):
|
||||
self.add_dir_to_zip(zf, f, prefix=arcname)
|
||||
else:
|
||||
zf.write(f, arcname)
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
def build_launchers(self):
|
||||
self.obj_dir = self.j(self.src_root, 'build', 'launcher')
|
||||
if not os.path.exists(self.obj_dir):
|
||||
os.makedirs(self.obj_dir)
|
||||
base = self.j(self.src_root, 'setup', 'installer', 'windows')
|
||||
|
146
setup/installer/windows/portable.c
Normal file
146
setup/installer/windows/portable.c
Normal file
@ -0,0 +1,146 @@
|
||||
#ifndef UNICODE
|
||||
#define UNICODE
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define BUFSIZE 4096
|
||||
|
||||
void show_error(LPCTSTR msg) {
|
||||
MessageBeep(MB_ICONERROR);
|
||||
MessageBox(NULL, msg, TEXT("Error"), MB_OK|MB_ICONERROR);
|
||||
}
|
||||
|
||||
void show_detailed_error(LPCTSTR preamble, LPCTSTR msg, int code) {
|
||||
LPTSTR buf;
|
||||
buf = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*
|
||||
(_tcslen(msg) + _tcslen(preamble) + 80));
|
||||
|
||||
_sntprintf_s(buf,
|
||||
LocalSize(buf) / sizeof(TCHAR), _TRUNCATE,
|
||||
TEXT("%s\r\n %s (Error Code: %d)\r\n"),
|
||||
preamble, msg, code);
|
||||
|
||||
show_error(buf);
|
||||
LocalFree(buf);
|
||||
}
|
||||
|
||||
void show_last_error_crt(LPCTSTR preamble) {
|
||||
TCHAR buf[BUFSIZE];
|
||||
int err = 0;
|
||||
|
||||
_get_errno(&err);
|
||||
_wcserror_s(buf, BUFSIZE, err);
|
||||
show_detailed_error(preamble, buf, err);
|
||||
}
|
||||
|
||||
void show_last_error(LPCTSTR preamble) {
|
||||
TCHAR *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),
|
||||
&msg,
|
||||
0, NULL );
|
||||
|
||||
show_detailed_error(preamble, msg, (int)dw);
|
||||
}
|
||||
|
||||
|
||||
LPTSTR get_app_dir() {
|
||||
LPTSTR buf, buf2, buf3;
|
||||
DWORD sz;
|
||||
TCHAR drive[4] = TEXT("\0\0\0");
|
||||
errno_t err;
|
||||
|
||||
buf = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
||||
buf2 = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
||||
buf3 = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
||||
|
||||
sz = GetModuleFileName(NULL, buf, BUFSIZE);
|
||||
|
||||
if (sz == 0 || sz > BUFSIZE-1) {
|
||||
show_error(TEXT("Failed to get path to calibre-portable.exe"));
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
err = _tsplitpath_s(buf, drive, 4, buf2, BUFSIZE, NULL, 0, NULL, 0);
|
||||
|
||||
if (err != 0) {
|
||||
show_last_error_crt(TEXT("Failed to split path to calibre-portable.exe"));
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
_sntprintf_s(buf3, BUFSIZE-1, _TRUNCATE, TEXT("%s%s"), drive, buf2);
|
||||
free(buf); free(buf2);
|
||||
return buf3;
|
||||
}
|
||||
|
||||
void launch_calibre(LPCTSTR exe, LPCTSTR config_dir, LPCTSTR library_dir) {
|
||||
DWORD dwFlags=0;
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
BOOL fSuccess;
|
||||
TCHAR cmdline[BUFSIZE];
|
||||
|
||||
if (! SetEnvironmentVariable(TEXT("CALIBRE_CONFIG_DIRECTORY"), config_dir)) {
|
||||
show_last_error(TEXT("Failed to set environment variables"));
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
dwFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP;
|
||||
_sntprintf_s(cmdline, BUFSIZE, _TRUNCATE, TEXT(" \"--with-library=%s\""), library_dir);
|
||||
|
||||
ZeroMemory( &si, sizeof(si) );
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory( &pi, sizeof(pi) );
|
||||
|
||||
fSuccess = CreateProcess(exe, cmdline,
|
||||
NULL, // Process handle not inheritable
|
||||
NULL, // Thread handle not inheritable
|
||||
FALSE, // Set handle inheritance to FALSE
|
||||
dwFlags, // Creation flags http://msdn.microsoft.com/en-us/library/ms684863(v=vs.85).aspx
|
||||
NULL, // Use parent's environment block
|
||||
NULL, // Use parent's starting directory
|
||||
&si, // Pointer to STARTUPINFO structure
|
||||
&pi // Pointer to PROCESS_INFORMATION structure
|
||||
);
|
||||
|
||||
if (fSuccess == 0) {
|
||||
show_last_error(TEXT("Failed to launch the calibre program"));
|
||||
}
|
||||
|
||||
// Close process and thread handles.
|
||||
CloseHandle( pi.hProcess );
|
||||
CloseHandle( pi.hThread );
|
||||
|
||||
}
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
||||
{
|
||||
LPTSTR app_dir, config_dir, exe, library_dir;
|
||||
|
||||
app_dir = get_app_dir();
|
||||
config_dir = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
||||
library_dir = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
||||
exe = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
||||
|
||||
_sntprintf_s(config_dir, BUFSIZE, _TRUNCATE, TEXT("%sCalibre Settings"), app_dir);
|
||||
_sntprintf_s(exe, BUFSIZE, _TRUNCATE, TEXT("%sCalibre\\calibre.exe"), app_dir);
|
||||
_sntprintf_s(library_dir, BUFSIZE, _TRUNCATE, TEXT("%sCalibre Library"), app_dir);
|
||||
|
||||
launch_calibre(exe, config_dir, library_dir);
|
||||
|
||||
free(app_dir); free(config_dir); free(exe); free(library_dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,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.gz'%(__appname__, __version__))
|
||||
installers.append('dist/%s-portable-%s.zip'%(__appname__, __version__))
|
||||
return installers
|
||||
|
||||
def installer_description(fname):
|
||||
@ -38,6 +39,8 @@ def installer_description(fname):
|
||||
return 'Windows installer'
|
||||
if fname.endswith('.dmg'):
|
||||
return 'OS X dmg'
|
||||
if fname.endswith('.zip'):
|
||||
return 'Calibre Portable'
|
||||
return 'Unknown file'
|
||||
|
||||
class ReUpload(Command): # {{{
|
||||
|
Loading…
x
Reference in New Issue
Block a user