[7900] | 1 | """ |
---|
| 2 | Create a Windows installer EXE. |
---|
| 3 | |
---|
| 4 | usage: create_win_installer [<revision>] |
---|
| 5 | |
---|
| 6 | where <revision> is an optional revision number - if not supplied then |
---|
| 7 | update through svn and use latest revision |
---|
| 8 | |
---|
| 9 | This program expects to be run from the root directory of the ANUGA branch |
---|
| 10 | being released. |
---|
| 11 | |
---|
| 12 | This script works only on Windows! |
---|
| 13 | """ |
---|
| 14 | |
---|
| 15 | |
---|
| 16 | import sys |
---|
| 17 | import os |
---|
| 18 | import getopt |
---|
| 19 | import tempfile |
---|
| 20 | import shutil |
---|
| 21 | |
---|
| 22 | import dirs_to_distribute |
---|
| 23 | from anuga.utilities.data_audit_wrapper import IP_verified |
---|
| 24 | |
---|
| 25 | from anuga.utilities.system_tools import get_user_name, get_host_name |
---|
| 26 | from anuga.abstract_2d_finite_volumes.util import get_revision_number |
---|
| 27 | from anuga.abstract_2d_finite_volumes.util import store_version_info |
---|
| 28 | from anuga.config import major_revision |
---|
| 29 | |
---|
| 30 | |
---|
| 31 | # name of 'home' directory containing releases |
---|
| 32 | ANUGA_RELEASE_DIR = 'anuga_releases' |
---|
| 33 | |
---|
| 34 | # prefix for temporary directory |
---|
| 35 | TEMP_DIR_PREFIX = 'anuga_release_' |
---|
| 36 | |
---|
| 37 | # path to and required contents of documentation directory |
---|
| 38 | DOC_PATH = os.path.join('anuga_core', 'documentation', 'user_manual') |
---|
[8011] | 39 | DOC_FILES = ['anuga_user_manual.pdf', |
---|
[7900] | 40 | 'anuga_installation_guide.pdf', |
---|
| 41 | 'anuga_whats_new.pdf'] |
---|
| 42 | |
---|
| 43 | |
---|
| 44 | # path to NSIS install directory |
---|
| 45 | NSIS_PATH = r'C:\Program Files\NSIS' |
---|
| 46 | NSIS_EXE = os.path.join(NSIS_PATH, 'makensis.exe') |
---|
| 47 | |
---|
| 48 | # the name of the subversion commandline client |
---|
| 49 | SVN_CLIENT = 'svn' |
---|
| 50 | |
---|
| 51 | |
---|
| 52 | def abort(msg, stop=True): |
---|
| 53 | """Some sort of error, report and abort.""" |
---|
| 54 | |
---|
| 55 | xlog('*' * 80) |
---|
| 56 | xlog(msg) |
---|
| 57 | xlog('*' * 80) |
---|
| 58 | if stop: |
---|
| 59 | sys.exit(10) |
---|
| 60 | |
---|
| 61 | |
---|
| 62 | def check_doc(temp_dir, release_name): |
---|
| 63 | """Make sure all the documentation is present. |
---|
| 64 | |
---|
| 65 | temp_dir released code working directory |
---|
| 66 | release_name ANUGA release name |
---|
| 67 | |
---|
| 68 | Returns if everything OK. |
---|
| 69 | """ |
---|
| 70 | |
---|
| 71 | bad = False |
---|
| 72 | |
---|
| 73 | for file in DOC_FILES: |
---|
| 74 | doc_file = os.path.join(DOC_PATH, file) |
---|
| 75 | if not os.path.isfile(doc_file): |
---|
| 76 | xlog('Documentation file %s is missing.' % doc_file) |
---|
| 77 | bad = True |
---|
| 78 | |
---|
| 79 | if bad and not Force: |
---|
| 80 | abort('Sorry, documentation files are missing.\n' |
---|
[7902] | 81 | 'You must release on Linux before Windows.') |
---|
[7900] | 82 | |
---|
| 83 | |
---|
| 84 | def check_IP(temp_dir, verbose=False): |
---|
| 85 | """Check IP statements in release directories.""" |
---|
| 86 | |
---|
| 87 | if not IP_verified(temp_dir, verbose=verbose): |
---|
| 88 | if not Force: |
---|
| 89 | abort('Files have not been verified for IP.\n' |
---|
| 90 | 'Each data file must have a licence file with it.') |
---|
| 91 | |
---|
| 92 | |
---|
| 93 | def compile_nsi_file(): |
---|
| 94 | """Compile the installer NSI file.""" |
---|
| 95 | |
---|
| 96 | # figure out where the *.nsi file actually is |
---|
| 97 | nsi_file = os.path.join(BranchDir, 'installation_files', 'windows', |
---|
| 98 | 'installer.nsi') |
---|
| 99 | |
---|
| 100 | do_cmd('"%s" /V2 %s' % (NSIS_EXE, nsi_file)) |
---|
| 101 | |
---|
| 102 | |
---|
| 103 | def copy_release_directories(revision, temp_dir): |
---|
| 104 | """Get release dirs (correct version) into release directory.""" |
---|
| 105 | |
---|
| 106 | for src in dirs_to_distribute.dirmap: |
---|
| 107 | dst = os.path.join(temp_dir, dirs_to_distribute.dirmap[src]) |
---|
| 108 | do_cmd('%s export -r %s --quiet %s %s' |
---|
| 109 | % (SVN_CLIENT, revision, src, dst)) |
---|
| 110 | |
---|
| 111 | |
---|
| 112 | def do_cmd(cmd, ignore_errors=False): |
---|
| 113 | """Execute a command, optionally ignore errors.""" |
---|
| 114 | |
---|
| 115 | try: |
---|
| 116 | xlog(cmd) |
---|
| 117 | os.system(cmd) |
---|
| 118 | except: |
---|
| 119 | if not ignore_errors: |
---|
| 120 | raise |
---|
| 121 | |
---|
| 122 | |
---|
| 123 | def get_directories(rel_name): |
---|
| 124 | """Create release and temporary directories, return paths. |
---|
| 125 | |
---|
| 126 | rel_name the ANUGA release name |
---|
| 127 | """ |
---|
| 128 | |
---|
| 129 | # create the release directory |
---|
| 130 | rel_area = os.path.join('~', ANUGA_RELEASE_DIR) |
---|
| 131 | rel_area = os.path.expanduser(rel_area) |
---|
| 132 | |
---|
| 133 | rel_dir = os.path.join(rel_area, rel_name) |
---|
| 134 | if os.path.isdir(rel_dir): |
---|
| 135 | if Force: |
---|
| 136 | os.removedirs(rel_dir) |
---|
| 137 | else: |
---|
| 138 | abort("Sorry, release directory '%s' already exists.\n" |
---|
| 139 | "Please delete that directory first." % rel_dir) |
---|
| 140 | |
---|
| 141 | os.makedirs(rel_dir) |
---|
| 142 | |
---|
| 143 | # create a temporary scratch directory |
---|
| 144 | temp_dir = tempfile.mkdtemp(suffix='', prefix=TEMP_DIR_PREFIX) |
---|
| 145 | |
---|
| 146 | return (rel_dir, temp_dir) |
---|
| 147 | |
---|
| 148 | |
---|
| 149 | def get_release_name(): |
---|
| 150 | """Get release information and create release name. |
---|
| 151 | |
---|
| 152 | Get release numbers from the current directory. |
---|
| 153 | |
---|
| 154 | Returns a string which is of the form 'anuga-X.Y.Z' where X is the |
---|
| 155 | major release number, Y is the minor and Z is the bug number. |
---|
| 156 | """ |
---|
| 157 | |
---|
| 158 | curr_dir = os.getcwd() |
---|
| 159 | curr_dir = os.path.basename(curr_dir) |
---|
| 160 | split_dir = curr_dir.split('_') |
---|
| 161 | if len(split_dir) < 2: |
---|
| 162 | abort('You must run this script in an ANUGA branch directory.') |
---|
| 163 | if split_dir[0] != 'anuga': |
---|
| 164 | abort('You must run this script in an ANUGA branch directory.') |
---|
| 165 | |
---|
| 166 | major = split_dir[1] |
---|
| 167 | if len(split_dir) < 3: |
---|
| 168 | minor = '0' |
---|
| 169 | else: |
---|
| 170 | minor = split_dir[2] |
---|
| 171 | |
---|
| 172 | if len(split_dir) < 4: |
---|
| 173 | bug = '0' |
---|
| 174 | else: |
---|
| 175 | bug = split_dir[3] |
---|
| 176 | |
---|
| 177 | return 'anuga-%s.%s.%s' % (major, minor, bug) |
---|
| 178 | |
---|
| 179 | |
---|
| 180 | def get_svn_revision(): |
---|
| 181 | """Get the current revision number from svn. |
---|
| 182 | |
---|
| 183 | This uses the CollabNet subversion client. |
---|
| 184 | """ |
---|
| 185 | |
---|
| 186 | try: |
---|
| 187 | fid = os.popen('%s info' % SVN_CLIENT) |
---|
| 188 | except: |
---|
| 189 | abort('Subversion commandline client not found') |
---|
| 190 | |
---|
| 191 | lines = fid.readlines() |
---|
| 192 | fid.close() |
---|
| 193 | |
---|
| 194 | for l in lines: |
---|
| 195 | l = l.strip() |
---|
| 196 | if l.startswith('Last Changed Rev:'): |
---|
| 197 | (_, revision) = l.split(':', 1) |
---|
| 198 | try: |
---|
| 199 | revision = int(revision) |
---|
| 200 | except ValueError: |
---|
| 201 | abort('"svn info" did not return expected line: ' + l) |
---|
| 202 | return revision |
---|
| 203 | |
---|
| 204 | abort('"svn info" did not return expected line "Last Changed Rev:"') |
---|
| 205 | |
---|
| 206 | |
---|
| 207 | def make_installer(revision): |
---|
| 208 | """Make a windows installer. |
---|
| 209 | |
---|
| 210 | revision the revision number (string) to release |
---|
| 211 | (may be None, meaning 'use latest') |
---|
| 212 | """ |
---|
| 213 | |
---|
| 214 | global BranchDir |
---|
| 215 | |
---|
| 216 | # get the current branch directory path |
---|
| 217 | BranchDir = os.getcwd() |
---|
| 218 | |
---|
| 219 | # if revision is None (not supplied) do update and get latest revision |
---|
| 220 | if revision is None: |
---|
| 221 | do_cmd('%s up' % SVN_CLIENT) |
---|
| 222 | revision = get_svn_revision() |
---|
| 223 | |
---|
| 224 | # get release name from current directory |
---|
| 225 | release_name = get_release_name() |
---|
| 226 | |
---|
| 227 | # get working directory paths (directories have been created) |
---|
| 228 | (release_dir, temp_dir) = get_directories(release_name) |
---|
| 229 | |
---|
| 230 | # get the ANUGA directories that are being released |
---|
| 231 | copy_release_directories(revision, temp_dir) |
---|
| 232 | |
---|
| 233 | # check IP data for release directories |
---|
| 234 | check_IP(temp_dir, verbose=True) |
---|
| 235 | |
---|
| 236 | # generate the LaTeX documentation |
---|
| 237 | check_doc(temp_dir, release_name) |
---|
| 238 | |
---|
| 239 | # create ZIP release file, put into release dir |
---|
| 240 | make_zip_file(temp_dir, release_name, release_dir) |
---|
| 241 | |
---|
| 242 | # compile Windows *.nsi file |
---|
| 243 | # creates EXE in <BranchDir>\installation_files\windows |
---|
| 244 | # check we have required files/directories |
---|
| 245 | # remove all generated *.exe files |
---|
| 246 | anuga_dir = os.path.join(BranchDir, 'installation_files', 'windows', |
---|
| 247 | 'files', release_name) |
---|
| 248 | if not os.path.isdir(anuga_dir): |
---|
| 249 | abort("Sorry, directory %s doesn't exist.\n" |
---|
| 250 | "You need to run the Linux create distribution first." |
---|
| 251 | % anuga_dir) |
---|
| 252 | |
---|
| 253 | |
---|
| 254 | src = os.path.join(BranchDir, 'installation_files', 'windows', '*.exe') |
---|
| 255 | do_cmd('del /Q %s' % src) |
---|
| 256 | compile_nsi_file() |
---|
| 257 | |
---|
| 258 | # copy the create EXE to the release directory |
---|
| 259 | do_cmd('move /Y %s %s' % (src, release_dir)) |
---|
| 260 | |
---|
| 261 | # clean up |
---|
| 262 | shutil.rmtree(temp_dir) |
---|
| 263 | |
---|
| 264 | |
---|
| 265 | def make_zip_file(temp_dir, release_name, release_dir): |
---|
| 266 | """Make a ZIP file, copy to release directory. |
---|
| 267 | |
---|
| 268 | temp_dir directory where we copied release files |
---|
| 269 | release_name the release name |
---|
| 270 | release_dir destination for the ZIP file |
---|
| 271 | """ |
---|
| 272 | |
---|
| 273 | # create .tgz file in temp_dir |
---|
| 274 | |
---|
| 275 | |
---|
| 276 | |
---|
| 277 | def usage(msg=None): |
---|
| 278 | """Give befuddled user some help.""" |
---|
| 279 | |
---|
| 280 | if msg: |
---|
| 281 | print(msg) |
---|
| 282 | print('usage: create_win_installer [<revision_number>]\n' |
---|
| 283 | 'where <revision_number> is an optional revision number to release.') |
---|
| 284 | |
---|
| 285 | |
---|
| 286 | def xlog(msg): |
---|
| 287 | """Print and log a message.""" |
---|
| 288 | |
---|
| 289 | print(msg) |
---|
| 290 | |
---|
| 291 | # uncomment this to get file logging |
---|
| 292 | ## log(msg) |
---|
| 293 | |
---|
| 294 | |
---|
| 295 | if __name__ == '__main__': |
---|
| 296 | global Force |
---|
| 297 | |
---|
| 298 | if sys.platform != 'win32': |
---|
| 299 | msg = ('This script is not written for Linux. ' |
---|
| 300 | 'Please run it on a Windows platform.') |
---|
| 301 | raise Exception(msg) |
---|
| 302 | |
---|
| 303 | argv = sys.argv[1:] |
---|
| 304 | |
---|
| 305 | try: |
---|
| 306 | opts, args = getopt.getopt(argv, 'hf', ['help', 'force']) |
---|
| 307 | except getopt.error, msg: |
---|
| 308 | usage() |
---|
| 309 | sys.exit(10) |
---|
| 310 | |
---|
| 311 | Force = False |
---|
| 312 | for (opt, param) in opts: |
---|
| 313 | if opt in ['-h', '--help']: |
---|
| 314 | usage() |
---|
| 315 | sys.exit(0) |
---|
| 316 | elif opt in ['-f', '--force']: |
---|
| 317 | Force = True |
---|
| 318 | |
---|
| 319 | if len(args) > 1: |
---|
| 320 | usage() |
---|
| 321 | sys.exit(10) |
---|
| 322 | |
---|
| 323 | revision = None |
---|
| 324 | if len(args) == 1: |
---|
| 325 | try: |
---|
| 326 | revision = int(args[0]) |
---|
| 327 | except ValueError: |
---|
| 328 | usage() |
---|
| 329 | sys.exit(10) |
---|
| 330 | |
---|
| 331 | make_installer(revision) |
---|