summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/pythontex/pythontex_install.py
blob: 64ac0e4d1b88742a62491fb3ca5d8f78274507e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''
Install PythonTeX

This installation script is written to work with TeX Live and MiKTeX.  Note
that PythonTeX is included in TeX Live 2013 and later, and may be installed 
via the package manager.  Thus, this installation script is only needed with 
TeX Live when you wish to install the latest version.  PythonTeX is not 
currently available via the MiKTeX package manager.

The script will automatically overwrite (and thus update) all previously 
installed PythonTeX files in the designated installation location.  When 
Kpathsea is available, files may be installed in TEXMFDIST, TEXMFLOCAL, 
TEXMFHOME, or a manually specified location.  Otherwise, the installation 
location must be specified manually.  Installing in TEXMFDIST is useful 
under TeX Live if you want to install PythonTeX and then update it in the 
future via the package manager.

The `mktexlsr` (TeX Live) or `initexmf --update-fndb` (MiKTeX) command is 
executed at the end of the script, to make the system aware of any new files.

Under TeX Live, the script attempts to create a binary wrapper (Windows) or 
symlink (Linux and OS X) for launching the main PythonTeX scripts, 
`pythontex*.py` and `depythontex*.py`.  Under MiKTeX, it attempts to create
a batch file in `miktex/bin`.


Copyright (c) 2012-2014, Geoffrey M. Poore
All rights reserved.
Licensed under the BSD 3-Clause License:
    http://www.opensource.org/licenses/BSD-3-Clause

'''


# Imports
import sys
import platform
from os import path, mkdir, makedirs
if platform.system() != 'Windows':
    # Only create symlinks if not under Windows 
    # (os.symlink doesn't exist under Windows)
    from os import symlink, chmod, unlink
from subprocess import call, check_call, check_output
from shutil import copy
import textwrap


# We need a version of input that works under both Python 2 and 3
try:
    input = raw_input
except:
    pass


# Print startup messages and notices
print('Preparing to install PythonTeX')
if platform.system() != 'Windows':
    message = '''
              You may need to run this script with elevated permissions
              and/or specify the environment.  For example, you may need
              "sudo env PATH=$PATH".  That is typically necessary when your
              system includes a TeX distribution, and you have manually
              installed another distribution (common with Ubuntu etc.).  If 
              the installation path you want is not automatically detected, 
              it may indicate a permissions issue.              
              '''
    print(textwrap.dedent(message))


# Attempt to detect the TeX distribution
try:
    if sys.version_info.major == 2:
        texout = check_output(['latex', '--version'])
    else:
        texout = check_output(['latex', '--version']).decode('utf-8')
except:
    sys.exit('Could not retrieve latex info when running "latex --version"')
if 'TeX Live' in texout:
    detected_texdist = True
    texlive = True
    miktex = False
elif platform.system() == 'Windows' and 'MiKTeX' in texout:
    detected_texdist = True
    texlive = False
    miktex = True
else:
    detected_texdist = False
    texlive = False
    miktex = False


# Make sure all necessary files are present
# The pythontex_gallery and pythontex_quickstart are optional; we 
# check for them when installing doc, and install if available
needed_files = ['pythontex.py', 'pythontex2.py', 'pythontex3.py',
                'pythontex_engines.py', 'pythontex_utils.py',
                'depythontex.py', 'depythontex2.py', 'depythontex3.py',
                'pythontex.sty', 'pythontex.ins', 'pythontex.dtx', 
                'pythontex.pdf', 'README',
                'syncpdb.py']
missing_files = False
# Print a list of all files that are missing, and exit if any are
for eachfile in needed_files:
    if not path.exists(eachfile):
        print('Could not find file ' + eachfile)
        missing_files = True
if missing_files:
    sys.exit('Exiting due to missing files.')


# Retrieve the location of valid TeX trees
if sys.version_info[0] == 2:
    try:
        texmf_dist = check_output(['kpsewhich', '-var-value', 'TEXMFDIST']).rstrip('\r\n')
    except:
        texmf_dist = None
    try:
        texmf_local = check_output(['kpsewhich', '-var-value', 'TEXMFLOCAL']).rstrip('\r\n')
    except:
        texmf_local = None
    try:
        texmf_home = check_output(['kpsewhich', '-var-value', 'TEXMFHOME']).rstrip('\r\n')
    except:
        texmf_home = None
else:
    try:
        texmf_dist = check_output(['kpsewhich', '-var-value', 'TEXMFDIST']).decode('utf-8').rstrip('\r\n')
    except:
        texmf_dist = None
    try:
        texmf_local = check_output(['kpsewhich', '-var-value', 'TEXMFLOCAL']).decode('utf-8').rstrip('\r\n')
    except:
        texmf_local = None
    try:
        texmf_home = check_output(['kpsewhich', '-var-value', 'TEXMFHOME']).decode('utf-8').rstrip('\r\n')
    except:
        texmf_home = None


# Get installation location from user
texmf_vars = [texmf_dist, texmf_local, texmf_home]
message = '''
          Choose an installation location.
          
          TEXMFDIST is a good choice if you want to update PythonTeX 
          in the future using your TeX distribution's package manager
          (assuming that is supported).
          
            1. TEXMFDIST
                 {0}
            2. TEXMFLOCAL
                 {1}
            3. TEXMFHOME
                 {2}
            4. Manual location
            
            5. Exit without installing
          '''.format(*[x if x else '<INVALID>' for x in texmf_vars])

if any(texmf_vars):
    path_choice = ''
    while (path_choice not in ('1', '2', '3', '4', '5') or 
            (int(path_choice) <= 3 and not texmf_vars[int(path_choice)-1])):
        print(textwrap.dedent(message))
        path_choice = input('Installation location (number):  ')
        if path_choice == '':
            sys.exit()
    if path_choice == '1':
        texmf_path = texmf_dist
    elif path_choice == '2':
        texmf_path = texmf_local
    elif path_choice == '3':
        texmf_path = texmf_home
    elif path_choice == '4':
        texmf_path = input('Enter a path:\n')
        if texmf_path == '':
            sys.exit()
        if platform.system() == 'Windows':
            if 'texlive' in texmf_path.lower():
                detected_texdist = True
                texlive = True
                miktex = False
            elif 'miktex' in texmf_path.lower():
                detected_texdist = True
                texlive = False
                miktex = True
    else:
        sys.exit()
else:
    print('Failed to detect possible installation locations automatically.')
    print('TEXMF paths could not be located with kpsewhich.')
    texmf_path = input('Plese enter an installation path, or press "Enter" to exit:\n')
    if texmf_path == '':
        sys.exit()

# Make sure path slashes are compatible with the operating system
# Kpathsea returns forward slashes, but Windows needs back slashes
texmf_path = path.expandvars(path.expanduser(path.normcase(texmf_path)))

# Check to make sure the path is valid 
# This should only be needed for manual input, but it's a good check
if not path.isdir(texmf_path):
    sys.exit('Invalid installation path.  Exiting.')

# Now check that all other needed paths are present
if path_choice != '2':
    doc_path = path.join(texmf_path, 'doc', 'latex')
    package_path = path.join(texmf_path, 'tex', 'latex')
    scripts_path = path.join(texmf_path, 'scripts')
    source_path = path.join(texmf_path, 'source', 'latex')
else:
    doc_path = path.join(texmf_path, 'doc', 'latex', 'local')
    package_path = path.join(texmf_path, 'tex', 'latex', 'local')
    scripts_path = path.join(texmf_path, 'scripts', 'local')
    source_path = path.join(texmf_path, 'source', 'latex', 'local')
# May need to create some local directories
make_paths = False
for eachpath in [doc_path, package_path, scripts_path, source_path]:
    if not path.exists(eachpath):
        if make_paths:
            makedirs(eachpath)
            print('  * Created ' + eachpath)
        else:
            choice = ''
            while choice not in ('y', 'n'):
                choice = input('Some directories do not exist.  Create them? [y/n]  ')
                if choice == '':
                    sys.exit()
            if choice == 'y':
                make_paths = True
                try:
                    makedirs(eachpath)
                    print('  * Created ' + eachpath)
                except (OSError, IOError) as e:
                    if e.errno == 13:
                        print('\nInsufficient permission to install PythonTeX')
                        if platform.system() == 'Windows':
                            message = '''
                                      You may need to run the installer as "administrator".
                                      This may be done under Vista and later by right-clicking on
                                      pythontex_install.bat, then selecting "Run as administrator".
                                      Or you can open a command prompt as administrator 
                                      (Start, Programs, Accessories, right-click Command Prompt,
                                      Run as administrator), change to the directory in which
                                      pythontex_install.py is located, and run 
                                      "python pythontex_install.py".
                                      '''
                            print(textwrap.dedent(message))
                            call(['pause'], shell=True)
                        else:
                            print('(For example, you may need "sudo", or possibly "sudo env PATH=$PATH")\n')
                        sys.exit(1)
                    else:
                        raise
            else:
                message = '''
                          Paths were not created.  The following will be needed.
                            * {0}
                            * {1}
                            * {2}
                            * {3}
                          
                          Exiting.
                          '''.format(doc_path, package_path, scripts_path, source_path)
                print(textwrap.dedent(message))
                sys.exit()

# Modify the paths by adding the pythontex directory, which will be created
doc_path = path.join(doc_path, 'pythontex')
package_path = path.join(package_path, 'pythontex')
scripts_path = path.join(scripts_path, 'pythontex')
source_path = path.join(source_path, 'pythontex')


# Install files
# Use a try/except in case elevated permissions are needed (Linux and OS X)
print('\nPythonTeX will be installed in \n  ' + texmf_path)
try:
    # Install docs
    if not path.exists(doc_path):
        mkdir(doc_path)
    copy('pythontex.pdf', doc_path)
    copy('README', doc_path)
    for doc in ('pythontex_quickstart.tex', 'pythontex_quickstart.pdf', 
                'pythontex_gallery.tex', 'pythontex_gallery.pdf'):
        if path.isfile(doc):
            copy(doc, doc_path)
        else:
            doc = path.join('..', doc.rsplit('.', 1)[0], doc)
            if path.isfile(doc):
                copy(doc, doc_path)
    # Install package
    if not path.exists(package_path):
        mkdir(package_path)
    copy('pythontex.sty', package_path)
    # Install scripts
    if not path.exists(scripts_path):
        mkdir(scripts_path)
    copy('pythontex.py', scripts_path)
    copy('depythontex.py', scripts_path)
    copy('pythontex_utils.py', scripts_path)
    copy('pythontex_engines.py', scripts_path)
    copy('syncpdb.py', scripts_path)
    for ver in [2, 3]:
        copy('pythontex{0}.py'.format(ver), scripts_path)
        copy('depythontex{0}.py'.format(ver), scripts_path)
    # Install source
    if not path.exists(source_path):
        mkdir(source_path)
    copy('pythontex.ins', source_path)
    copy('pythontex.dtx', source_path)
except (OSError, IOError) as e:
    if e.errno == 13:
        print('\nInsufficient permission to install PythonTeX')
        if platform.system() == 'Windows':
            message = '''
                      You may need to run the installer as "administrator".
                      This may be done under Vista and later by right-clicking on
                      pythontex_install.bat, then selecting "Run as administrator".
                      Or you can open a command prompt as administrator 
                      (Start, Programs, Accessories, right-click Command Prompt,
                      Run as administrator), change to the directory in which
                      pythontex_install.py is located, and run 
                      "python pythontex_install.py".
                      '''
            print(textwrap.dedent(message))
            call(['pause'], shell=True)
        else:
            print('(For example, you may need "sudo", or possibly "sudo env PATH=$PATH")\n')
        sys.exit(1)
    else:
        raise        


# Install binary wrappers, create symlinks, or suggest the creation of 
# wrappers/batch files/symlinks.  This part is operating system dependent.
if platform.system() == 'Windows':
    # If under Windows, we create a binary wrapper if under TeX Live 
    # or a batch file if under MiKTeX.  Otherwise, alert the user 
    # regarding the need for a wrapper or batch file.    
    if miktex:
        try:
            if sys.version_info.major == 2:
                bin_path = check_output(['kpsewhich', '-var-value', 'TEXMFDIST']).rstrip('\r\n')
            else:
                bin_path = check_output(['kpsewhich', '-var-value', 'TEXMFDIST']).decode('utf-8').rstrip('\r\n')
            bin_path = path.join(bin_path, 'miktex', 'bin')
            
            for s in ('pythontex.py', 'depythontex.py'):
                batch = '@echo off\n"{0}" %*\n'.format(path.join(scripts_path, s))
                f = open(path.join(bin_path, s.replace('.py', '.bat')), 'w')
                f.write(batch)
                f.close()
        except:
            message = '''
                      Could not create a batch file for launching pythontex.py and 
                      depythontex.py.  You will need to create a batch file manually.
                      Sample batch files are included with the main PythonTeX files.
                      The batch files should be in a location on the Windows PATH.
                      The bin/ directory in your TeX distribution may be a good 
                      location.
                      
                      The scripts pythontex.py and depythontex.py are located in 
                      the following directory:
                        {0}
                      '''.format(scripts_path)
            print(textwrap.dedent(message))
    else:
        # Assemble the binary path, assuming TeX Live
        # The directory bin/ should be at the same level as texmf
        bin_path = path.join(path.split(texmf_path)[0], 'bin', 'win32') 
        if path.exists(path.join(bin_path, 'runscript.exe')):
            for f in ('pythontex.py', 'depythontex.py'):
                copy(path.join(bin_path, 'runscript.exe'), path.join(bin_path, '{0}.exe'.format(f.rsplit('.')[0])))
            print('\nCreated binary wrapper...')
        else:
            message = '''
                      Could not create a wrapper for launching pythontex.py and 
                      depythontex.py; did not find runscript.exe.  You will need 
                      to create a wrapper manually, or use a batch file.  Sample 
                      batch files are included with the main PythonTeX files.  
                      The wrapper or batch file should be in a location on the 
                      Windows PATH.  The bin/ directory in your TeX distribution 
                      may be a good location.
                      
                      The scripts pythontex.py and depythontex.py are located in 
                      the following directory:
                        {0}
                      '''.format(scripts_path)
            print(textwrap.dedent(message))
else:
    # Optimistically proceed as if every system other than Windows can 
    # share one set of code.
    root_path = path.split(texmf_path)[0]
    # Create a list of all possible subdirectories of bin/ for TeX Live
    # Source:  http://www.tug.org/texlive/doc/texlive-en/texlive-en.html#x1-250003.2.1
    texlive_platforms = ['alpha-linux', 'amd64-freebsd', 'amd64-kfreebsd',
                         'armel-linux', 'i386-cygwin', 'i386-freebsd',
                         'i386-kfreebsd', 'i386-linux', 'i386-solaris',
                         'mips-irix', 'mipsel-linux', 'powerpc-aix', 
                         'powerpc-linux', 'sparc-solaris', 'universal-darwin',
                         'x86_64-darwin', 'x86_64-linux', 'x86_64-solaris']
    symlink_created = False
    # Try to create a symlink in the standard TeX Live locations
    for pltfrm in texlive_platforms:
        bin_path = path.join(root_path, 'bin', pltfrm)
        if path.exists(bin_path):
            # Unlink any old symlinks if they exist, and create new ones
            # Not doing this gave permissions errors under Ubuntu
            for f in ('pythontex.py', 'pythontex2.py', 'pythontex3.py',
                      'depythontex.py', 'depythontex2.py', 'depythontex3.py'):
                link = path.join(bin_path, f)
                if path.exists(link):
                    unlink(link)
                symlink(path.join(scripts_path, f), link)
                chmod(link, 0o775)
            symlink_created = True
    
    # If the standard TeX Live bin/ locations didn't work, try the typical 
    # location for MacPorts TeX Live.  This should typically be 
    # /opt/local/bin, but instead of assuming that location, we just climb 
    # two levels up from texmf-dist and then look for a bin/ directory that
    # contains a tex executable.  (For MacPorts, texmf-dist should be at 
    # /opt/local/share/texmf-dist.)
    if not symlink_created and platform.system() == 'Darwin':
        bin_path = path.join(path.split(root_path)[0], 'bin')
        if path.exists(bin_path):
            try:
                # Make sure this bin/ is the bin/ we're looking for, by
                # seeing if pdftex exists
                check_output([path.join(bin_path, 'pdftex'), '--version'])
                # Create symlinks
                for f in ('pythontex.py', 'pythontex2.py', 'pythontex3.py',
                          'depythontex.py', 'depythontex2.py', 'depythontex3.py'):
                    link = path.join(bin_path, f)
                    if path.exists(link):
                        unlink(link)
                    symlink(path.join(scripts_path, f), link)
                    chmod(link, 0o775)
                symlink_created = True
            except:
                pass
    if symlink_created:
        print("\nCreated symlink in Tex's bin/ directory...")
    else:
        print('\nCould not automatically create a symlink to pythontex*.py and depythontex*.py.')
        print('You may wish to create one manually, and make it executable via chmod.')
        print('The scripts pythontex*.py and depythontex*.py are located in the following directory:')
        print('    ' + scripts_path)


# Alert TeX to the existence of the package via mktexlsr
if not miktex:
    try:
        # Need to adjust if under Windows with a user-specified TeX Live
        # installation and a default MiKTeX installation; want to call
        # mktexlsr for the user-specified TeX Live installation
        if platform.system() == 'Windows' and 'MiKTeX' in texout:
            check_call(path.join(bin_path, 'mktexlsr'))
        else:
            check_call(['mktexlsr'])
        print('\nRunning "mktexlsr" to make TeX aware of new files...')
    except:
        print('Could not run "mktexlsr".')
        print('Your system may not be aware of newly installed files.')
else:
    success = False
    try:
        check_call(['initexmf', '--admin', '--update-fndb'])
        print('\nRunning "initexmf --admin --update-fndb" to make TeX aware of new files...')
        check_call(['initexmf', '--update-fndb'])
        print('\nRunning "initexmf --update-fndb" to make TeX aware of new files...')
        success = True
    except:
        pass
    if not success:
        try:
            check_call(['initexmf', '--update-fndb'])
            print('\nRunning "initexmf --update-fndb" to make TeX aware of new files...')
            print('Depending on your installation settings, you may also need to run')
            print('"initexmf --admin --update-fndb"')
        except:
            print('Could not run "initexmf --update-fndb" or "initexmf --admin --update-fndb"')
            print('Your system may not be aware of newly installed files.')

            
if platform.system() == 'Windows':
    # Pause so that the user can see any errors or other messages
    # input('\n[Press ENTER to exit]')
    print('\n')
    call(['pause'], shell=True)