diff --git a/.gitignore b/.gitignore index 0cd8a23c45..44f0a3871e 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ cmd/ethereum/ethereum cmd/mist/mist +deploy/osx/Mist.app +deploy/osx/Mist\ Installer.dmg diff --git a/deploy/.gitignore b/deploy/.gitignore new file mode 100644 index 0000000000..ea796b7cbb --- /dev/null +++ b/deploy/.gitignore @@ -0,0 +1,16 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile ~/.gitignore_global + +/tmp +*/**/*un~ +*un~ +.DS_Store +*/**/.DS_Store +ethereum/ethereum +ethereal/ethereal +osx/Mist.app +osx/__MACOSX +osx/Mist.dmg diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000000..2686911030 --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,55 @@ +go-build +======== + +Build scripts for Ethereum Mist. + +### OS X +Install all build dependencies. + +* brew install go gmp readline +* npm install -g appdmg +* go get -u github.com/ethereum/go-ethereum/cmd/mist +* Open build.py and edit the (macdeployqt) paths. +* python build.py + +If everything went ok you should now have a Mist.dmg file in your current folder. + +### Windows + +Install all build dependencies. + +* [Golang](http://golang.org/dl/) 1.2 or higher (32-bit required) +* Install [Git](http://git-scm.com/) and [Mercurial](http://mercurial.selenic.com/) +* [MinGW32](http://www.mingw.org/) (add X:\MinGW\bin directory to your PATH) +* Use mingw32-get to install *gmp* packages +* Install [Qt5 for Windows 32-bit MinGW](http://qt-project.org/downloads) (5.2.1 at the moment of writing) +* Install [pkg-config](http://www.freedesktop.org/wiki/Software/pkg-config/) somewhere in your PATH. (read the [instructions](http://stackoverflow.com/questions/1710922/how-to-install-pkg-config-in-windows) here) +* Install [NSIS](http://nsis.sourceforge.net/) + +After all these things have been satisfied ```go get -u github.com/ethereum/go-ethereum/cmd/mist``` + +Once the compilation is completed you can create a setup binary. +- Edit build.bat and change qtPath and mingwPath to the paths of your installed versions. +- right-click the nsi file and select "Compile NSIS Script". + +If everything went well you should now have a windows-setup file. + + +#### Troubleshooting + +Now it will be a miracle if the windows build works in one go. +So here are some possible solutions for things that can go wrong: + +*Expected unqualified-id* +``` +qopenglversionfunctions.h:785:43: error: expected unqualified-id before ')' token + void (QOPENGLF_APIENTRYP MemoryBarrier)(GLbitfield barriers); +``` + +See [this ticket](https://github.com/go-qml/qml/issues/56) for a couple of solutions. + +*pkg-config path* + +pkg-config might complain about the config path. Setup an environment value `PKG_CONFIG_PATH` and set it to `C:\Qt\Qt5.2.1\5.2.1\mingw48_32\lib\pkgconfig`. Adopted to your QT version. + +If there are any build problems please create an issue. diff --git a/deploy/osx/Ethereal.icns b/deploy/osx/Ethereal.icns new file mode 100644 index 0000000000..b02ad0ca3e Binary files /dev/null and b/deploy/osx/Ethereal.icns differ diff --git a/deploy/osx/Mist.icns b/deploy/osx/Mist.icns new file mode 100644 index 0000000000..3ba75a4327 Binary files /dev/null and b/deploy/osx/Mist.icns differ diff --git a/deploy/osx/background-source-pm.pxm b/deploy/osx/background-source-pm.pxm new file mode 100644 index 0000000000..4cf97f8eaf Binary files /dev/null and b/deploy/osx/background-source-pm.pxm differ diff --git a/deploy/osx/background-source.psd b/deploy/osx/background-source.psd new file mode 100644 index 0000000000..8fb1b33ce3 Binary files /dev/null and b/deploy/osx/background-source.psd differ diff --git a/deploy/osx/background.png b/deploy/osx/background.png new file mode 100644 index 0000000000..54bbab35d3 Binary files /dev/null and b/deploy/osx/background.png differ diff --git a/deploy/osx/background@2x.png b/deploy/osx/background@2x.png new file mode 100644 index 0000000000..20bbc6acea Binary files /dev/null and b/deploy/osx/background@2x.png differ diff --git a/deploy/osx/build.py b/deploy/osx/build.py new file mode 100644 index 0000000000..f5e731efd9 --- /dev/null +++ b/deploy/osx/build.py @@ -0,0 +1,238 @@ +import sys, os, argparse, logging, shutil, subprocess, stat,glob +from os.path import isfile + +# TODO handle icns +# TODO create dmg +# TODO Add client qml files and png files +# CHMOD +x the main binary + +logging.basicConfig( + stream=sys.stdout, + format='%(asctime)s : %(levelname)s\t : %(message)s', + datefmt='%m/%d/%Y %I:%M:%S %p', + level=logging.DEBUG +) + +XML_PLIST = """ + + + + + CFBundleGetInfoString + Mist + CFBundleExecutable + Mist + CFBundleIdentifier + com.ethereum.mist + CFBundleName + Mist + CFBundleIconFile + Mist.icns + CFBundleShortVersionString + POC8 + CFBundleInfoDictionaryVersion + POC8 + CFBundlePackageType + APPL + IFMajorVersion + 0 + IFMinorVersion + 5 + + +""" + +RUN_SCRIPT =""" +#!/bin/bash +cd "${0%/*}" +./go-ethereum +""" + +class AppBundler: + def copytree(self, src, dst, symlinks=False, ignore=None): + for item in os.listdir(src): + s = os.path.join(src, item) + d = os.path.join(dst, item) + if os.path.isdir(s): + shutil.copytree(s, d, symlinks, ignore) + else: + shutil.copy2(s, d) + + # If macdeployqt handles qmldir then runs on app + def runMacDeployQT(self): + exe = '/usr/local/opt/qt5/bin/macdeployqt' + if not os.path.exists(exe): exe = 'macdeployqt' + p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + handles_qml = False + for line in p.stdout.readlines(): + if '-qmldir=' in line: + handles_qml = True + break + if handles_qml and self.go_path is not None: + qml_path = os.path.join(self.go_path, 'src/github.com/ethereum/go-ethereum/cmd/mist/assets/qml/') #TODO this is terrible + out = os.path.join(self.output_dir + '/Mist.app') + command = exe + ' ' + out + ' -executable='+out+'/Contents/MacOS/Mist' + ' -qmldir=' + qml_path #TODO this is terrible + logging.info('Running macdeployqt with options') + p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + for line in p.stdout.readlines(): + logging.info('macdeployqt: ' + line.strip()) + else: + logging.error('Your version of macdeployqt does not handle qmldir') + + # Add ICNS file to + def insertICNS(self): + path = os.path.join(self.output_dir, 'Mist.app/Contents/Resources/Mist.icns') + + try: + shutil.copyfile('./Mist.icns',path) # TODO this is horrible + logging.info('Inserted Mist.icns') + except Exception as e: + logging.error(str(e)) + + def insertQMLnPNG(self): + pass # TODO + + #def signApp(self): + # after macdeployqt copy /usr/local/opt/qt5/lib/QtCore.framework/Contents/Info.plist to .app/Contents/Resources/QtCore.framework/Resources/Info.plist + # codesign --verbose --force --sign "Developer ID Application: <>" /Users/_/Dropbox/Experiments/EthereumBuild/Ethereal.app/Contents/Frameworks/QtCore.framework + # do for rest + # codesign --verbose --deep --force --sign "Developer ID Application: <>" Ethereal.app + # codesign --verify --verbose=4 Ethereal.app + + def insertAssets(self): + asset_path = os.path.join(self.go_path, 'src/github.com/ethereum/go-ethereum/cmd/mist/assets') + self.copytree(asset_path,"Mist.app/Contents/Resources/") + # Copy mnemonic word list + #shutil.copy(os.path.join(self.go_path, 'src/github.com/ethereum/eth-go/ethcrypto/mnemonic.words.lst'),"Mist.app/Contents/Resources/") + + # Insert all QML files and other resource files Mist needs + def insertResources(self): + qml_path = os.path.join(self.go_path, 'src/github.com/ethereum/go-ethereum/cmd/mist/assets/qml/') + target_folder = "Mist.app/Contents/Resources/" + target_folder_qml = target_folder + "qml/" + + os.makedirs(target_folder_qml) + + files = glob.glob(qml_path) + for f in files: + print "Copying %s to %s" % (f, target_folder_qml) + if isfile(f): + shutil.copy(f, target_folder_qml) + else: + self.copytree(f, target_folder_qml) + + files = glob.glob(os.path.join(self.go_path, 'src/github.com/ethereum/go-ethereum/cmd/mist/assets/*')) + for f in files: + print "Copying %s to %s" % (f, target_folder) + if isfile(f): + shutil.copy(f, target_folder) + else: + self.copytree(f, target_folder) + # Finds go-etherum binary and copies to app bundle + + def insertGoBinary(self): + if self.go_path is not None: + binary = os.path.join(self.go_path, 'bin/mist') + if os.path.exists(binary): + try: + shutil.copyfile(binary, os.path.join(self.output_dir, 'Mist.app/Contents/MacOS/Mist')) # TODO this is horrible + os.chmod(os.path.join(self.output_dir, 'Mist.app/Contents/MacOS/Mist'), 0711) + logging.info('Inserted go-ethereum binary') + except Exception as e: + logging.error(str(e)) + else: + logging.error('Cannot find go-etherum binary') + if self.handleHumanInput('Run "go get -u github.com/ethereum/go-ethereum" ?'): + logging.debug('Not Implemented') + pass + else: + logging.error('GOPATH not found, cannot continue') + + # Write the Info.plist + def writePList(self): + try: + with open(os.path.join(self.output_dir, 'Mist.app/Contents/Info.plist'), 'wb') as f: # TODO this is horrible + f.write(XML_PLIST) + f.close() + logging.info('Info.plist written') + except Exception as e: + logging.error(str(e)) + + # Building out directory structure + def buildStructure(self, root, structure): + if root is not self.output_dir: + try: + os.mkdir(root) + logging.info('Created ' + root) + except Exception as e: + logging.error(str(e)) + if self.handleHumanInput('Remove Directory?'): + try: + shutil.rmtree(root) + self.buildStructure(root, structure) + return + except Exception as e: + logging.error(str(e)) + for item in structure.keys(): + self.buildStructure( + os.path.join(root, item), + structure[item] + ) + + # Convert human input to boolean + def handleHumanInput(self, question=''): + if self.force: return True + try: + answer = raw_input(question + " [Y/n]: ").lower() + except: + return True + if answer is '' or answer[0:1] == 'y': return True + return False + + logging.info('Copying QTWebProcess') + libexec_path = self.output_dir + '/Mist.app/Contents/libexec' + try: + os.mkdir(libexec_path) + shutil.copy2(path, libexec_path) + return True + except OSError as e: + print("Problem getting QTWebprocess on path %s. Error: %s" % (path, e)) + return False + + # Setup Variables + def __init__(self, args): + self.force = args['force'] + self.output_dir = args['output'] + self.app_name = "".join(x for x in args['name'] if x.isalnum()) # Simple Santize + self.app_structure = { + '%s.app' % self.app_name : { + 'Contents' : { + 'MacOS' : {}, + 'Resources' : {} + } + } + } + self.go_path = os.environ.get('GOPATH') + self.buildStructure(self.output_dir, self.app_structure) + self.writePList() + self.insertICNS() + self.insertGoBinary() + self.insertAssets() + + #self.insertResources() + + self.runMacDeployQT() + os.system("sh script.sh " + self.output_dir + "/Mist.app/Contents") + os.system("appdmg dmg_spec.json Mist.dmg") + + logging.info("fin'") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Standalone Mist Go Client App Bundler') + parser.add_argument('-n','--name', help='Name of app bundle', default='Mist', required=False) + parser.add_argument('-q','--qtwebpath', help='Location of QtWebProcess', default='Mist', required=False) + parser.add_argument('-o','--output', help='Directory to write app bundle', default=os.getcwd(), required=False) + parser.add_argument('-f','--force', help='Force Fresh Build', default=False, required=False) + args = vars(parser.parse_args()) + AppBundler(args) diff --git a/deploy/osx/dmg_spec.json b/deploy/osx/dmg_spec.json new file mode 100644 index 0000000000..c433124963 --- /dev/null +++ b/deploy/osx/dmg_spec.json @@ -0,0 +1,10 @@ +{ + "title": "Mist Installer", + "icon": "Mist.icns", + "background": "background.png", + "icon-size": 144, + "contents": [ + { "x": 900, "y": 244, "type": "link", "path": "/Applications" }, + { "x": 140, "y": 244, "type": "file", "path": "Mist.app" } + ] +} diff --git a/deploy/osx/script.sh b/deploy/osx/script.sh new file mode 100644 index 0000000000..345ab5ae84 --- /dev/null +++ b/deploy/osx/script.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +BUILD_FOLDER_PATH=$1 +BUILD_QML_FOLDER_PATH="$BUILD_FOLDER_PATH/Resources/qml" +BUILD_PLUGINS_FOLDER_PATH="$BUILD_FOLDER_PATH/PlugIns" + +declare -a BROKEN_FILES; +k=0; +for j in $(find ${BUILD_QML_FOLDER_PATH} -name *.dylib); do + BROKEN_FILES[${k}]=$j + + ((k=k+1)) +done + + +for i in "${BROKEN_FILES[@]}"; do + REPLACE_STRING="$BUILD_FOLDER_PATH/" + APP_CONTENT_FILE=${i//$REPLACE_STRING/""} + IFS='/' read -a array <<< "$APP_CONTENT_FILE" + LENGTH=${#array[@]} + LAST_ITEM_INDEX=$((LENGTH-1)) + FILE=${array[${LENGTH} - 1]} + + ORIGINE_PATH=$(find ${BUILD_PLUGINS_FOLDER_PATH} -name ${FILE}) + ORIGINE_PATH=${ORIGINE_PATH//$REPLACE_STRING/""} + s="" + for((l=0;l<${LAST_ITEM_INDEX};l++)) do + s=$s"../" + done + s=$s$ORIGINE_PATH + echo "s: $s" + + REMOVE_BROKEN_ALIAS=$(rm -rf $i) + RESULT=$(ln -s $s $i) +done diff --git a/deploy/windows/build.bat b/deploy/windows/build.bat new file mode 100644 index 0000000000..b67a6024de --- /dev/null +++ b/deploy/windows/build.bat @@ -0,0 +1,39 @@ +@ECHO off +set origin=%GOPATH%\src\github.com\ethereum\go-ethereum\cmd\mist +set ethgo=%GOPATH%\src\github.com\ethereum\eth-go +set target=%GOPATH%\pkg\ethereum\ +set qtPath=C:\Qt\Qt5.2.1\5.2.1\mingw48_32 +set mingwPath=C:\MingW\ + +ECHO "Setting up Windows binaries" + +ECHO "Copying assets from %GOPATH%" +xcopy /s %origin%\assets %target%assets + +ECHO "Copying mnemonic words" +copy %ethgo%\ethcrypto\mnemonic.words.lst %target% + +ECHO "Compiling Mist" +cd %origin% +go install + +ECHO "Copying to source folder" +copy %GOPATH%\bin\mist.exe %target% + +ECHO "Running QT Deploy" +cd %target% +windeployqt --webkit2 --qmldir=assets\qml\ . + +echo "Copy missing DLLs & Others" + +copy %qtPath%\bin\QtWebProcess.exe %target% +copy %qtPath%\bin\Qt5MultimediaWidgets.dll %target% +copy %qtPath%\bin\Qt5PrintSupport.dll %target% +copy %qtPath%\bin\Qt5OpenGL.dll %target% +copy %qtPath%\bin\Qt5WebKitWidgets.dll %target% + +copy %qtPath%\bin\libgcc_s_dw2-1.dll %target% +copy "%qtPath%\bin\libstdc++-6.dll" %target% +copy %qtPath%\bin\libwinpthread-1.dll %target% + +copy %mingwPath%\bin\libgmp-10.dll %target% diff --git a/deploy/windows/create-setup.nsi b/deploy/windows/create-setup.nsi new file mode 100644 index 0000000000..ed2fdc29a6 --- /dev/null +++ b/deploy/windows/create-setup.nsi @@ -0,0 +1,111 @@ +;-------------------------------- +;Include Modern UI + + !include "MUI2.nsh" + +;-------------------------------- +;General + + ;Name and file + Name "Mist" + OutFile "mist-setup.exe" + + ;Default installation folder + InstallDir "$PROGRAMFILES\Mist" + + ;Get installation folder from registry if available + InstallDirRegKey HKCU "Software\Mist" "" + + ;Request application privileges for Windows Vista + RequestExecutionLevel admin + + SetCompressor /SOLID lzma ; had the strongest compression rate for Mist + +;-------------------------------- +;Variables + +;-------------------------------- +;Interface Settings + + + !define MUI_ICON "logo.ico" + !define MUI_HEADERIMAGE + !define MUI_HEADERIMAGE_BITMAP "ethereum.bmp" + !define MUI_HEADERIMAGE_RIGHT + !define MUI_ABORTWARNING + +;-------------------------------- +;Pages + + ;!insertmacro MUI_PAGE_LICENSE "tmp/LICENCE" + ;!insertmacro MUI_PAGE_COMPONENTS + !insertmacro MUI_PAGE_DIRECTORY + + ;Start Menu Folder Page Configuration + !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU" + !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\Mist" + !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" + + ;!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder + + !insertmacro MUI_PAGE_INSTFILES + + !insertmacro MUI_UNPAGE_CONFIRM + !insertmacro MUI_UNPAGE_INSTFILES + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" + +;-------------------------------- +;Installer Sections + +Section + + SetOutPath "$INSTDIR" + + file /r $%GOPATH%\pkg\ethereum\*.* + file logo.ico + + ;Store installation folder + WriteRegStr HKCU "Software\Mist" "" $INSTDIR + + ;Create uninstaller + WriteUninstaller "$INSTDIR\Uninstall.exe" + + + CreateShortCut "$DESKTOP\Mist.lnk" "$INSTDIR\mist.exe" "" "$INSTDIR\logo.ico" 0 + + ;create start-menu items + CreateDirectory "$SMPROGRAMS\Mist" + CreateShortCut "$SMPROGRAMS\Mist\Uninstall.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 + CreateShortCut "$SMPROGRAMS\Mist\Mist.lnk" "$INSTDIR\mist.exe" "" "$INSTDIR\logo.ico" 0 + +SectionEnd + +;-------------------------------- +;Descriptions + + ;Assign language strings to sections + ;!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + ; !insertmacro MUI_DESCRIPTION_TEXT ${SecDummy} $(DESC_SecDummy) + ;!insertmacro MUI_FUNCTION_DESCRIPTION_END + +;-------------------------------- +;Uninstaller Section + +Section "Uninstall" + + ;ADD YOUR OWN FILES HERE... + RMDir /r "$INSTDIR\*.*" + + RMDir "$INSTDIR" + + Delete "$DESKTOP\Mist.lnk" + Delete "$SMPROGRAMS\Mist\*.*" + RmDir "$SMPROGRAMS\Mist" + + DeleteRegKey /ifempty HKCU "Software\Mist" + +SectionEnd diff --git a/deploy/windows/ethereum.bmp b/deploy/windows/ethereum.bmp new file mode 100644 index 0000000000..7c52420932 Binary files /dev/null and b/deploy/windows/ethereum.bmp differ diff --git a/deploy/windows/logo.ico b/deploy/windows/logo.ico new file mode 100644 index 0000000000..bc85a69ddc Binary files /dev/null and b/deploy/windows/logo.ico differ