summaryrefslogtreecommitdiff
path: root/winbuild/builder.py
blob: 1e60b63ba0bc32ba0c3f2f56bf3d6b79c1f32697 (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
import os, os.path, shutil, sys, subprocess
from .utils import *
from .config import *

class Batch(object):
    def __init__(self, bconf):
        self.bconf = bconf
        self.commands = []
        
        self.add(self.vcvars_cmd)
        self.add('echo on')
        if self.bconf.vc_version == 'vc14':
            # I don't know why vcvars doesn't configure this under vc14
            self.add('set include=%s\\include;%%include%%' % self.bconf.windows_sdk_path)
            if self.bconf.bitness == 32:
                self.add('set lib=%s\\lib;%%lib%%' % self.bconf.windows_sdk_path)
                self.add('set path=%s\\bin;%%path%%' % self.bconf.windows_sdk_path)
            else:
                self.add('set lib=%s\\lib\\x64;%%lib%%' % self.bconf.windows_sdk_path)
                self.add('set path=%s\\bin\\x64;%%path%%' % self.bconf.windows_sdk_path)
        self.add(self.nasm_cmd)
        
        self.add('set path=%s;%%path%%' % self.bconf.extra_bin_paths[self.bconf.bitness]['rc_bin'])
        
    def add(self, cmd):
        self.commands.append(cmd)
        
    # if patch fails to apply hunks, it exits with nonzero code.
    # if patch doesn't find the patch file to apply, it exits with a zero code!
    ERROR_CHECK = 'IF %ERRORLEVEL% NEQ 0 exit %errorlevel%'

    def batch_text(self):
        return ("\n" + self.ERROR_CHECK + "\n").join(self.commands)

    @property
    def vcvars_bitness_parameter(self):
        params = {
            32: 'x86',
            64: 'amd64',
        }
        return params[self.bconf.bitness]

    @property
    def vcvars_relative_path(self):
        return 'vc/vcvarsall.bat'

    @property
    def vc_path(self):
        if self.bconf.vc_version in self.bconf.vc_paths and self.bconf.vc_paths[self.bconf.vc_version]:
            path = self.bconf.vc_paths[self.bconf.vc_version]
            if not os.path.join(path, self.vcvars_relative_path):
                raise Exception('vcvars not found in specified path')
            return path
        else:
            for path in self.bconf.default_vc_paths[self.bconf.vc_version]:
                if os.path.exists(os.path.join(path, self.vcvars_relative_path)):
                    return path
            raise Exception('No usable vc path found')

    @property
    def vcvars_path(self):
        return os.path.join(self.vc_path, self.vcvars_relative_path)

    @property
    def vcvars_cmd(self):
        # https://msdn.microsoft.com/en-us/library/x4d2c09s.aspx
        return "call \"%s\" %s" % (
            self.vcvars_path,
            self.vcvars_bitness_parameter,
        )

    @property
    def nasm_cmd(self):
        return "set path=%s;%%path%%\n" % self.bconf.nasm_path

class Builder(object):
    def __init__(self, **kwargs):
        self.bconf = kwargs.pop('bconf')
        self.use_dlls = False

    @contextlib.contextmanager
    def execute_batch(self):
        batch = Batch(self.bconf)
        yield batch
        with open('doit.bat', 'w') as f:
            f.write(batch.batch_text())
        if False:
            print("Executing:")
            with open('doit.bat', 'r') as f:
                print(f.read())
            sys.stdout.flush()
        rv = subprocess.call(['doit.bat'])
        if rv != 0:
            print("\nFailed to execute the following commands:\n")
            with open('doit.bat', 'r') as f:
                print(f.read())
            sys.stdout.flush()
            exit(3)

class StandardBuilder(Builder):
    @property
    def state_tag(self):
        return self.output_dir_path

    @property
    def bin_path(self):
        return os.path.join(self.bconf.archives_path, self.output_dir_path, 'dist', 'bin')

    @property
    def include_path(self):
        return os.path.join(self.bconf.archives_path, self.output_dir_path, 'dist', 'include')

    @property
    def lib_path(self):
        return os.path.join(self.bconf.archives_path, self.output_dir_path, 'dist', 'lib')

    @property
    def dll_paths(self):
        raise NotImplementedError

    @property
    def builder_name(self):
        return self.__class__.__name__.replace('Builder', '').lower()
        
    @property
    def my_version(self):
        return getattr(self.bconf, '%s_version' % self.builder_name)

    @property
    def output_dir_path(self):
        return '%s-%s-%s' % (self.builder_name, self.my_version, self.bconf.vc_tag)
        
    def standard_fetch_extract(self, url_template):
        url = url_template % dict(
            my_version=self.my_version,
        )
        fetch(url)
        archive_basename = os.path.basename(url)
        archive_name = archive_basename.replace('.tar.gz', '')
        untar(self.bconf, archive_name)
        
        suffixed_dir = self.output_dir_path
        if os.path.exists(suffixed_dir):
            shutil.rmtree(suffixed_dir)
        os.rename(archive_name, suffixed_dir)
        return suffixed_dir