1 # -*- coding: utf-8 -*-
3 ############################################################################
6 # This file is a SCons (http://www.scons.org/) builder #
7 # Copyright (c) 2012-14, Philipp Kraus, <philipp.kraus@flashpixx.de> #
8 # Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved. #
9 # This program is free software: you can redistribute it and/or modify #
10 # it under the terms of the GNU General Public License as #
11 # published by the Free Software Foundation, either version 3 of the #
12 # License, or (at your option) any later version. #
14 # This program is distributed in the hope that it will be useful, #
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
17 # GNU General Public License for more details. #
19 # You should have received a copy of the GNU General Public License #
20 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
21 ############################################################################
23 # the URLDownload-Builder can download any data from an URL into a target
24 # file. The target name is used are the file name of the downloaded file.
26 # This builder originated from work by Philipp Kraus and flashpixx project
27 # (see https://github.com/flashpixx). It has been modified to leverage
28 # the HTTP ETag header to be used as the csig. This allows the download
29 # builder to determine if the file should be downloaded again when the
30 # ETag header is supported
33 import urllib2, urlparse
34 import SCons.Builder, SCons.Node, SCons.Errors
36 # Define a source node to represent the remote file. The construction of the
37 # node will query the hosting site to get the ETag, size and last-modified
38 # date. This node also defines the method by which we will determine if
39 # the file should be downloaded again.
41 # This node derives from the Python.Value node
43 class URLNode(SCons.Node.Python.Value) :
44 def make_ready(self) :
46 stream = urllib2.urlopen( str(self.value) )
50 self.url_last_modified = None
51 self.url_content_length = None
54 self.url_etag = info['ETag']
55 if 'Last-Modified' in info :
56 self.url_last_modified = time.mktime(time.strptime(info['Last-Modified'], '%a, %d %b %Y %H:%M:%S GMT'))
57 if 'Content-Length' in info :
58 self.url_content_legth = info['Content-Length']
60 raise SCons.Errors.StopError( '%s [%s]' % (e, self.value) )
63 ninfo = self.get_ninfo()
66 ninfo.csig = self.url_etag
67 if self.url_last_modified :
68 ninfo.timestamp = self.url_last_modified
69 if self.url_content_length :
70 ninfo.size = self.url_content_length
71 SCons.Node.Node.visited(self);
73 def changed_since_last_build(self, target, prev_ni):
76 if prev_ni.csig == self.url_etag :
77 # print 'Matched on ETag:'+prev_ni.csig
80 if not self.url_last_modified :
81 # print 'Last modified date is not available'
83 if not self.url_content_length :
84 # print 'Content length is not available'
86 if prev_ni.timestamp != self.url_last_modified :
87 # print 'Modified since last build'
89 if prev_ni.size != self.url_content_length :
90 # print 'Content length has changed'
95 # print 'Not previous built'
98 # Creates the output message
99 # @param s original message
100 # @param target target name
101 # @param source source name
102 # @param env environment object
103 def __message( s, target, source, env ) :
104 print 'downloading [%s] from [%s] ...' % (target[0], source[0])
106 # Creates the action ie. the download function.
107 # This reads the data from the URL and writes it down to the file
108 # @param target target file on the local drive
109 # @param source URL for download
110 # @@param env environment object
111 def __action( target, source, env ) :
113 source_name = str(source[0])
114 target_name = str(target[0])
115 stream = urllib2.urlopen(source_name)
116 file = open( target_name, 'wb' )
117 file.write(stream.read())
120 # Change the access/modified time to match
121 # the date on the downloaded file, if available
122 ninfo = source[0].get_ninfo()
123 if hasattr(ninfo, 'timestamp') :
124 mtime = ninfo.timestamp
126 os.utime(target_name, (mtime, mtime))
127 except Exception, e :
128 raise SCons.Errors.StopError( '%s [%s]' % (e, source[0]) )
131 # Defines the emitter of the builder
132 # @param target target file on the local drive
133 # @param source URL for download
134 # @param env environment object
135 def __emitter( target, source, env ) :
136 return target, source
138 # generate function, that adds the builder to the environment,
139 # @param env environment object
140 def generate( env ) :
141 env['BUILDERS']['URLDownload'] = SCons.Builder.Builder( action = __action, emitter = __emitter, target_factory = SCons.Node.FS.File, source_factory = URLNode, single_source = True, PRINT_CMD_LINE_FUNC = __message )
143 # existing function of the builder
144 # @param env environment object