1# Copyright (C) 2009 Kevin Ollivier  All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions
5# are met:
6# 1. Redistributions of source code must retain the above copyright
7#    notice, this list of conditions and the following disclaimer.
8# 2. Redistributions in binary form must reproduce the above copyright
9#    notice, this list of conditions and the following disclaimer in the
10#    documentation and/or other materials provided with the distribution.
11#
12# THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
13# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
16# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
17# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
18# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
19# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
20# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23#
24# Helper functions for the WebKit build.
25
26import commands
27import glob
28import os
29import platform
30import re
31import shutil
32import sys
33import urllib
34import urlparse
35
36def get_output(command):
37    """
38    Windows-compatible function for getting output from a command.
39    """
40    if sys.platform.startswith('win'):
41        f = os.popen(command)
42        return f.read().strip()
43    else:
44        return commands.getoutput(command)
45
46def get_excludes(root, patterns):
47    """
48    Get a list of exclude patterns going down several dirs.
49    TODO: Make this fully recursive.
50    """
51    excludes = []
52
53    for pattern in patterns:
54        subdir_pattern = os.sep + '*'
55        for subdir in [subdir_pattern, subdir_pattern*2, subdir_pattern*3]:
56            adir = root + subdir + os.sep + pattern
57            files = glob.glob(adir)
58            for afile in files:
59                excludes.append(os.path.basename(afile))
60
61    return excludes
62
63def get_dirs_for_features(root, features, dirs):
64    """
65    Find which directories to include in the list of build dirs based upon the
66    enabled port(s) and features.
67    """
68    outdirs = dirs
69    for adir in dirs:
70        for feature in features:
71            relpath = os.path.join(adir, feature)
72            featuredir = os.path.join(root, relpath)
73            if os.path.exists(featuredir) and not relpath in outdirs:
74                outdirs.append(relpath)
75
76    return outdirs
77
78def download_if_newer(url, destdir):
79    """
80    Checks if the file on the server is newer than the one in the user's tree,
81    and if so, downloads it.
82
83    Returns the filename of the downloaded file if downloaded, or None if
84    the existing file matches the one on the server.
85    """
86    obj = urlparse.urlparse(url)
87    filename = os.path.basename(obj.path)
88    destfile = os.path.join(destdir, filename)
89
90    urlobj = urllib.urlopen(url)
91    size = long(urlobj.info().getheader('Content-Length'))
92
93    def download_callback(downloaded, block_size, total_size):
94        downloaded = block_size * downloaded
95        if downloaded > total_size:
96            downloaded = total_size
97        sys.stdout.write('%s %d of %d bytes downloaded\r' % (filename, downloaded, total_size))
98
99    # NB: We don't check modified time as Python doesn't yet handle timezone conversion
100    # properly when converting strings to time objects.
101    if not os.path.exists(destfile) or os.path.getsize(destfile) != size:
102        urllib.urlretrieve(url, destfile, download_callback)
103        print ''
104        return destfile
105
106    return None
107
108def update_wx_deps(conf, wk_root, msvc_version='msvc2008'):
109    """
110    Download and update tools needed to build the wx port.
111    """
112    import Logs
113    Logs.info('Ensuring wxWebKit dependencies are up-to-date.')
114
115    wklibs_dir = os.path.join(wk_root, 'WebKitLibraries')
116    waf = download_if_newer('http://wxwebkit.wxcommunity.com/downloads/deps/waf', os.path.join(wk_root, 'Tools', 'wx'))
117    if waf:
118        # TODO: Make the build restart itself after an update.
119        Logs.warn('Build system updated, please restart build.')
120        sys.exit(1)
121
122    # since this module is still experimental
123    wxpy_dir = os.path.join(wk_root, 'Source', 'WebKit', 'wx', 'bindings', 'python')
124    swig_module = download_if_newer('http://wxwebkit.wxcommunity.com/downloads/deps/swig.py.txt', wxpy_dir)
125    if swig_module:
126        shutil.copy(os.path.join(wxpy_dir, 'swig.py.txt'), os.path.join(wxpy_dir, 'swig.py'))
127
128    if sys.platform.startswith('win'):
129        Logs.info('downloading deps package')
130        archive = download_if_newer('http://wxwebkit.wxcommunity.com/downloads/deps/wxWebKitDeps-%s.zip' % msvc_version, wklibs_dir)
131        if archive and os.path.exists(archive):
132            os.system('unzip -o %s -d %s' % (archive, os.path.join(wklibs_dir, msvc_version)))
133
134    elif sys.platform.startswith('darwin'):
135        # export the right compiler for building the dependencies
136        if platform.release().startswith('10'): # Snow Leopard
137            os.environ['CC'] = conf.env['CC'][0]
138            os.environ['CXX'] = conf.env['CXX'][0]
139        os.system('%s/Tools/wx/install-unix-extras' % wk_root)
140
141def includeDirsForSources(sources):
142    include_dirs = []
143    for group in sources:
144        for source in group:
145            dirname = os.path.dirname(source)
146            if not dirname in include_dirs:
147                include_dirs.append(dirname)
148
149    return include_dirs
150
151def flattenSources(sources):
152    flat_sources = []
153    for group in sources:
154        flat_sources.extend(group)
155
156    return flat_sources
157
158def git_branch_name():
159    try:
160        branches = commands.getoutput("git branch --no-color")
161        match = re.search('^\* (.*)', branches, re.MULTILINE)
162        if match:
163            return ".%s" % match.group(1)
164    except:
165        pass
166
167    return ""
168
169def get_config(wk_root):
170    config_file = os.path.join(wk_root, 'WebKitBuild', 'Configuration')
171    config = 'Debug'
172
173    if os.path.exists(config_file):
174        config = open(config_file).read()
175
176    return config
177
178def svn_revision():
179    if os.system("git-svn info") == 0:
180        info = commands.getoutput("git-svn info ../..")
181    else:
182        info = commands.getoutput("svn info")
183
184    for line in info.split("\n"):
185        if line.startswith("Revision: "):
186            return line.replace("Revision: ", "").strip()
187
188    return ""
189