--- a/.hgignore +++ a/.hgignore @@ -19,7 +19,7 @@ ^\.project$ ^\.externalToolBuilders/.*$ ^hs_err_pid[0-9]*\.log -^nbbuild/antsrc/org/netbeans/nbbuild/extlibs/external\.pyc$ +^[^/]+/external/[^/]+\.(zip|jar|gz|bz2|gem|dll)$ ^o.apache.tools.ant.module/external/lib$ ^apisupport.project/src/org/netbeans/modules/apisupport/project/resources/sample-build-impl\.xml$ ^apisupport.project/src/org/netbeans/modules/apisupport/project/resources/sample-build\.xml$ --- a/.hgundead +++ a/.hgundead @@ -4,6 +4,7 @@ cnd.editor/build/classes/org/netbeans/modules/cnd/editor/Bundle.properties form/external/beansbinding-1.2.1-doc.zip form/external/beansbinding-1.2.1.jar +nbbuild/antsrc/org/netbeans/nbbuild/extlibs/external.pyc nbbuild/velocity.log websvc.saas.api/build/classes/org/netbeans/modules/websvc/saas/Bundle.properties websvc.saas.api/build/classes/org/netbeans/modules/websvc/saas/resources/layer.xml --- a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/DeregisterExternalHook.java +++ a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/DeregisterExternalHook.java @@ -0,0 +1,112 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.nbbuild.extlibs; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; + +/** + * Issue #141817: remove external.py registration. + */ +public class DeregisterExternalHook extends Task { + + private File root; + /** Location of NB source root. */ + public void setRoot(File root) { + this.root = root; + } + + @Override + public void execute() throws BuildException { + new File(root, "nbbuild/antsrc/org/netbeans/nbbuild/extlibs/external.pyc").delete(); + File[] repos = { + root, + new File(root, "contrib"), + }; + for (File repo : repos) { + File dotHg = new File(repo, ".hg"); + if (!dotHg.isDirectory()) { + log(repo + " is not a Mercurial repository", Project.MSG_VERBOSE); + continue; + } + try { + File hgrc = new File(dotHg, "hgrc"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + if (hgrc.isFile()) { + InputStream is = new FileInputStream(hgrc); + try { + byte[] buf = new byte[4096]; + int read; + while ((read = is.read(buf)) != -1) { + baos.write(buf, 0, read); + } + } finally { + is.close(); + } + } + String config = baos.toString(); + String newConfig = config. + replaceAll("(?m)^external *=.+\n", ""). + replaceAll("(?m)^\\*/external/\\*\\.\\{zip,jar,gz,bz2,gem,dll\\} = (up|down)load:.+\n", ""). + replaceAll("(^|\n)\n*(\\[(extensions|encode|decode)\\]\n+)+(?=\\[|$)", "$1"); + if (!newConfig.equals(config)) { + log("Unregister external hook from " + hgrc); + OutputStream os = new FileOutputStream(hgrc); + try { + os.write(config.getBytes()); + } finally { + os.close(); + } + } + } catch (IOException x) { + throw new BuildException(x, getLocation()); + } + } + } + +} --- a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/DownloadBinaries.java +++ a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/DownloadBinaries.java @@ -0,0 +1,332 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2007 Sun Microsystems, Inc. + */ + +package org.netbeans.nbbuild.extlibs; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.channels.FileChannel; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.util.FileUtils; + +/** + * Task to retrieve named files (generally large binaries such as ZIPs) from a repository. + * Similar to a very simplified version of Ivy, but correctly handles binaries + * with missing or irrelevant version numbers, since it is based on a hash of contents. + * You keep one or more manifests under version control which enumerate files and their SHA-1 hashes. + * Then just run this task to download any missing files. + * Remember to specify the binaries as "ignorable" to your version control system. + * You can also run it in a clean mode which will remove the binaries. + * At the end of this source file is a sample CGI script and matching form which you can run on the server + * to permit people to upload files to the correct repository paths. + * Motivation: http://wiki.netbeans.org/wiki/view/HgMigration#section-HgMigration-Binaries + */ +public class DownloadBinaries extends Task { + + private File cache; + /** + * Location of per-user cache of already downloaded binaries. + * Optional; no cache will be used if unset. + * The directory will be created if it does not yet exist. + */ + public void setCache(File cache) { + this.cache = cache; + } + + private String server; + /** + * URL prefix for the server repository. + * Should generally include a trailing slash. + * You may include multiple server URLs separated by spaces + * in which case they will be tried in order. + * To use a local repository, simply specify e.g. file:/repo/ as the URL. + */ + public void setServer(String server) { + this.server = server; + } + + private final List manifests = new ArrayList(); + /** + * Add one or more manifests of files to download. + * Each manifest is a text file; lines beginning with # are ignored. + * All other lines must be entries of the form + *
+     * 0123456789ABCDEF something-1.0.jar
+     * 
+ * consisting of an SHA-1 hash followed by a filename. + * The filename is relative to the manifest, usually a simple basename. + * If the file exists and has the specified hash, nothing is done. + * If it has the wrong hash, the task aborts with an error message. + * If it is missing, it is downloaded from the server (or copied from cache) + * using a filename derived from the basename of the file in the manifest and its hash. + * For example, the above line with a server of http://nowhere.net/repo/ + * would try to download + *
+     * http://nowhere.net/repo/0123456789ABCDEF-something-1.0.jar
+     * 
+ * Any version number etc. in the filename is purely informational; + * the "up to date" check is entirely based on the hash. + */ + public void addManifest(FileSet manifest) { + manifests.add(manifest); + } + + private boolean clean; + /** + * If true, rather than creating binary files, will delete them. + * Any cache is ignored in this case. + * If a binary does not match its hash, the build is aborted: + * the file might be a precious customized version and should not be blindly deleted. + */ + public void setClean(boolean clean) { + this.clean = clean; + } + + @Override + public void execute() throws BuildException { + for (FileSet fs : manifests) { + DirectoryScanner scanner = fs.getDirectoryScanner(getProject()); + File basedir = scanner.getBasedir(); + for (String include : scanner.getIncludedFiles()) { + File manifest = new File(basedir, include); + log("Scanning: " + manifest, Project.MSG_VERBOSE); + try { + BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(manifest), "UTF-8")); + String line; + while ((line = r.readLine()) != null) { + if (line.startsWith("#")) { + continue; + } + if (line.trim().length() == 0) { + continue; + } + String[] hashAndFile = line.split(" ", 2); + if (hashAndFile.length < 2) { + throw new BuildException("Bad line '" + line + "' in " + manifest, getLocation()); + } + File f = new File(manifest.getParentFile(), hashAndFile[1]); + if (!clean) { + if (!f.exists()) { + log("Creating " + f); + String cacheName = hashAndFile[0] + "-" + f.getName(); + if (cache != null) { + cache.mkdirs(); + File cacheFile = new File(cache, cacheName); + if (!cacheFile.exists()) { + download(cacheName, cacheFile); + } + try { + FileUtils.getFileUtils().copyFile(cacheFile, f); + } catch (IOException x) { + throw new BuildException("Could not copy " + cacheFile + " to " + f + ": " + x, x, getLocation()); + } + } else { + download(cacheName, f); + } + } + String actualHash = hash(f); + if (!actualHash.equals(hashAndFile[0])) { + throw new BuildException("File " + f + " requested by " + manifest + " to have hash " + + hashAndFile[0] + " actually had hash " + actualHash, getLocation()); + } + log("Have " + f + " with expected hash", Project.MSG_VERBOSE); + } else { + if (f.exists()) { + String actualHash = hash(f); + if (!actualHash.equals(hashAndFile[0])) { + throw new BuildException("File " + f + " requested by " + manifest + " to have hash " + + hashAndFile[0] + " actually had hash " + actualHash, getLocation()); + } + log("Deleting " + f); + f.delete(); + } + } + } + } catch (IOException x) { + throw new BuildException("Could not open " + manifest + ": " + x, x, getLocation()); + } + } + } + } + + private void download(String cacheName, File destination) { + if (server == null) { + throw new BuildException("Must specify a server to download files from", getLocation()); + } + for (String prefix : server.split(" ")) { + URL url; + try { + url = new URL(prefix + cacheName); + } catch (MalformedURLException x) { + throw new BuildException(x, getLocation()); + } + try { + URLConnection conn = url.openConnection(); + conn.connect(); + int code = HttpURLConnection.HTTP_OK; + if (conn instanceof HttpURLConnection) { + code = ((HttpURLConnection) conn).getResponseCode(); + } + if (code != HttpURLConnection.HTTP_OK) { + log("Skipping download from " + url + " due to response code " + code, Project.MSG_VERBOSE); + } + log("Downloading: " + url); + InputStream is = conn.getInputStream(); + try { + OutputStream os = new FileOutputStream(destination); + try { + byte[] buf = new byte[4096]; + int read; + while ((read = is.read(buf)) != -1) { + os.write(buf, 0, read); + } + os.close(); + } catch (IOException x) { + destination.delete(); + throw x; + } + } finally { + is.close(); + } + return; + } catch (IOException x) { + log("Could not download " + url + " to " + destination + ": " + x, Project.MSG_WARN); + } + } + throw new BuildException("Could not download " + cacheName + " from " + server, getLocation()); + } + + private String hash(File f) { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException x) { + throw new BuildException(x, getLocation()); + } + try { + FileInputStream is = new FileInputStream(f); + try { + digest.update(is.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, f.length())); + } finally { + is.close(); + } + } catch (IOException x) { + throw new BuildException("Could not get hash for " + f + ": " + x, x, getLocation()); + } + return String.format("%040X", new BigInteger(1, digest.digest())); + } + +} + +/* + +Sample upload script (edit repository location as needed): + +#!/usr/bin/env ruby +repository = '/tmp/repository' +require 'cgi' +require 'digest/sha1' +require 'date' +cgi = CGI.new +begin + if cgi.request_method == 'POST' + value = cgi['file'] + content = value.read + name = value.original_filename.gsub(/\.\.|[^a-zA-Z0-9._+-]/, '_') + sha1 = Digest::SHA1.hexdigest(content).upcase + open("#{repository}/#{sha1}-#{name}", "w") do |f| + f.write content + end + open("#{repository}/log", "a") do |f| + f << "#{DateTime.now.to_s} #{sha1}-#{name} #{cgi.remote_user}\n" + end + cgi.out do < + +Uploaded #{name} + + +

Uploaded. Add to your manifest:

+
#{sha1} #{name}
+ + +RESPONSE + end + else + cgi.out do <
+ +Upload a Binary + + + + + +
+ + +FORM + end + end +rescue + cgi.out do + "Caught an exception: #{$!}" + end +end + + */ --- a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/RegisterExternalHook.java +++ a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/RegisterExternalHook.java @@ -1,284 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - * - * Contributor(s): - * - * Portions Copyrighted 2008 Sun Microsystems, Inc. - */ - -package org.netbeans.nbbuild.extlibs; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.Task; -import org.netbeans.nbbuild.HgExec; - -/** - * Registers external.py as a Mercurial encode/decode hook, if appropriate. - */ -public class RegisterExternalHook extends Task { - - public RegisterExternalHook() {} - - private File root; - /** Location of NB source root. */ - public void setRoot(File root) { - this.root = root; - } - - private File hook; - /** Extension hook to register. */ - public void setHook(File hook) { - this.hook = hook; - } - - private static final String BINARIES = "*/external/*.{zip,jar,gz,bz2,gem,dll}"; - - @Override - public void execute() throws BuildException { - File dotHg = new File(root, ".hg"); - if (!dotHg.isDirectory()) { - log(root + " is not a Mercurial repository", Project.MSG_VERBOSE); - return; - } - List hgExecutable = HgExec.hgExecutable(); - try { - List commandAndArgs = new ArrayList(hgExecutable); - commandAndArgs.add("showconfig"); - Process p = new ProcessBuilder(commandAndArgs).directory(root).start(); - BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line; - while ((line = r.readLine()) != null) { - if (line.matches("(en|de)code\\.\\*\\*=.*")) { - // XXX try to fix it instead - // Tricky because no trivial way to find where Mercurial.ini is. - // If Hg installed to default Python path, could use: - // python -c 'from mercurial import util; print util.rcpath()' - // ({}** chosen because it sorts after */external/*.ext so will not take precedence.) - throw new BuildException( - "An existing global encode/decode hook will conflict with " + hook.getName() + "\n" + - "You must edit your Mercurial.ini and change '** = ...' in encode/decode sections to '{}** = ...'", getLocation()); - } - } - } catch (IOException x) { - log("Could not verify Hg configuration: " + x, Project.MSG_WARN); - } - File[] repos = { - root, - new File(root, "contrib"), - }; - for (File repo : repos) { - dotHg = new File(repo, ".hg"); - if (!dotHg.isDirectory()) { - log(repo + " is not a Mercurial repository", Project.MSG_VERBOSE); - continue; - } - try { - File hgrc = new File(dotHg, "hgrc"); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - if (hgrc.isFile()) { - InputStream is = new FileInputStream(hgrc); - try { - byte[] buf = new byte[4096]; - int read; - while ((read = is.read(buf)) != -1) { - baos.write(buf, 0, read); - } - } finally { - is.close(); - } - } - String config = baos.toString(); - if (Pattern.compile("(?m)^external *= *" + Pattern.quote(hook.getAbsolutePath()) + "$").matcher(config).find()) { - log("Hook already registered in " + hgrc, Project.MSG_VERBOSE); - continue; - } - if (Pattern.compile("(?m)^external *=").matcher(config).find()) { - log("Hook already registered in " + hgrc + " in wrong location, correcting...", Project.MSG_WARN); - config = config.replaceFirst("(?m)^external *=.*$", "external = " + hook.toString().replaceAll("\\\\", "\\\\\\\\")); - } else { - log("Registering hook in " + hgrc); - String nl = System.getProperty("line.separator"); - if (config.length() > 0) { - config += nl; - } - config += "[extensions]" + nl + "external = " + hook + nl + "[encode]" + nl; - Matcher m = Pattern.compile("https://[a-zA-Z0-9_]+(:[^@]+)?@hg.netbeans.org/").matcher(config); - if (m.find()) { - config += BINARIES + " = upload: " + m.group() + "binaries/upload" + nl; - } else { - config += "# To preauthenticate, use: https://jhacker:secret@hg.netbeans.org/binaries/upload" + nl + - BINARIES + " = upload: https://hg.netbeans.org/binaries/upload" + nl; - } - config += "[decode]" + nl + BINARIES + " = download: http://hg.netbeans.org/binaries/" + nl; - } - // XXX should also register hooks.pretxncommit.crlf=python:hgext.win32text.forbidcrlf (for Hg 1.0+ only) - OutputStream os = new FileOutputStream(hgrc); - try { - os.write(config.getBytes()); - } finally { - os.close(); - } - } catch (IOException x) { - throw new BuildException(x, getLocation()); - } - try { - log("Looking for external binaries in " + repo + " which need to be checked out again using decoder"); - List commandAndArgs = new ArrayList(hgExecutable); - commandAndArgs.add("locate"); - commandAndArgs.add(BINARIES); - Process p = new ProcessBuilder(commandAndArgs).directory(repo).start(); - BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); - boolean doCheckout = false; - String binary; - while ((binary = r.readLine()) != null) { - File f = new File(repo, binary); - log("Examining: " + f, Project.MSG_DEBUG); - if (!f.isFile()) { - log("No such file checked out: " + f, Project.MSG_VERBOSE); - doCheckout = true; // in case raw files got deleted last time - continue; - } - byte[] rawheader = "<< - -Uploaded #{name} - - -

Uploaded as:

-
#{sha1} #{name}
- - -RESPONSE - end - else - cgi.out do <
- -Upload a Binary - - - - - -
- - -FORM - end - end -rescue - cgi.out do - "Caught an exception: #{$!}" - end -end - - */ --- a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/external.py +++ a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/external.py @@ -1,222 +0,0 @@ -import os, re, urllib2, sha, inspect, sys -from mercurial import util, httprepo, ui - -if sys.version_info < (2, 4, 4): - ui.ui().warn('Warning: external hook requires Python 2.4.4+ (2.5.1 preferred)\n') - -# Workaround for a Python bug (in linecache.py?): -# http://bugs.python.org/issue1728 -# http://bugs.python.org/issue1665 -# https://bugs.launchpad.net/ubuntu/+source/python-defaults/+bug/70902 -# http://mail.python.org/pipermail/python-bugs-list/2007-March/037472.html -_inspect_findsource_orig = inspect.findsource -def _inspect_findsource_robust(object): - try: - return _inspect_findsource_orig(object) - except IndexError: - raise IOError('workaround for linecache bug') -inspect.findsource=_inspect_findsource_robust - -# Compatibility for Hg not including f8ad3b76e923: -def _findparamvalue(function, param): - for framerec in inspect.getouterframes(inspect.currentframe()): - if framerec[3] == function: - funcvars = inspect.getargvalues(framerec[0])[3] - if not param in funcvars: - raise util.Abort('No parameter named %s in function %s' % (param, function)) - return funcvars[param] - raise util.Abort('No function named %s in stack' % function) -def _filename(f): - if f: - return f - return _findparamvalue('_filter', 'filename') -def _ui(u): - if u: - return u - return _findparamvalue('_filter', 'self').ui -def _repo(r): - if r: - return r - return _findparamvalue('_filter', 'self') - -def _trim(filename): - return os.path.basename(filename) -def _cachedir(): - if 'HGEXTERNALCACHE' in os.environ: - d = os.environ['HGEXTERNALCACHE'] - else: - d = os.path.expanduser('~/.hgexternalcache') - if not os.path.exists(d): - os.makedirs(d) - return d -def _sha1hash(data): - return sha.new(data).hexdigest().upper() - -def _download_to_cache(url, ui, sha1, filename, cachefile): - ui.status('Downloading %s\n' % url) - handle = urllib2.urlopen(url) - data = handle.read() - handle.close() - if sha1 != _sha1hash(data): - raise util.Abort('hash mismatch in %s' % filename) - # XXX acquire write lock, or write to temp file and do atomic rename - handle = open(cachefile, 'wb') - handle.write(data) - handle.close() - return data - -def download(s, cmd, ui=None, filename=None, **kwargs): - # To support dev versions of Hg with f8ad3b76e923 but not f3a8b5360100: - cmd = re.sub(r'^download: *', '', cmd) - filename = _filename(filename) - ui = _ui(ui) - n = _trim(filename) - m = re.match(r'^<<>>\n$', s) - if not m: - raise util.Abort('malformed contents in %s' % filename) - if n != m.group(3): - raise util.Abort('incorrect basename in %s: %s' % (filename, m.group(3))) - cachefile = os.path.join(_cachedir(), m.group(1)) - if os.path.exists(cachefile): - handle = open(cachefile, 'rb') - data = handle.read() - handle.close() - if m.group(2) != _sha1hash(data): - try: - os.remove(cachefile) - except: - pass - raise util.Abort('hash mismatch in %s' % filename) - else: - data = _download_to_cache(url = cmd + m.group(1), - ui = ui, - sha1 = m.group(2), - filename = filename, - cachefile = cachefile) - return data - -def upload(s, cmd, ui=None, repo=None, filename=None, **kwargs): - cmd = re.sub(r'^upload: *', '', cmd) - filename = _filename(filename) - ui = _ui(ui) - repo = _repo(repo) - n = _trim(filename) - if not re.match(r'[a-zA-Z0-9._+-]+', n): - raise util.Abort('unsupported file basename: %s', filename) - sha1 = _sha1hash(s) - full = '%s-%s' % (sha1, n) - cachefile = os.path.join(_cachedir(), full) - if not os.path.exists(cachefile): - # XXX forces download and upload URLs to be related; - # would be better to get download URL from download filter, - # but that info is not trivially accessible here: - m = re.match(r'(https?://)(([^:@]+)(:([^@]+))?@)?(.+)upload', cmd) - if not m: - raise util.Abort('malformed upload URL: %s' % cmd) - downloadurl = m.group(1) + m.group(6) + full - url = m.group(1) + m.group(6) + 'upload' - try: - _download_to_cache(url = downloadurl, - ui = ui, - sha1 = sha1, - filename = filename, - cachefile = cachefile) - ui.status('No need to upload %s: %s already exists\n' % (filename, downloadurl)) - except IOError: - # probably a 404, this is normal - pm = httprepo.passwordmgr(ui) - pm.add_password(None, url, m.group(3), m.group(5)) - ui.status('Uploading %s to %s (%s Kb)\n' % (filename, url, len(s) / 1024)) - auth = urllib2.HTTPBasicAuthHandler(pm) - try: - data = {'file': repo.wfile(filename)} - # XXX support proxies; look at httprepo.httprepository.__init__ - # or http://www.hackorama.com/python/upload.shtml - urllib2.build_opener(MultipartPostHandler, auth).open(url, data).close() - except IOError, err: - raise util.Abort('Problem uploading %s to %s (try it manually using a web browser): %s\n' % (filename, url, err)) - # Now ensure that the upload actually worked, by downloading: - _download_to_cache(url = downloadurl, - ui = ui, - sha1 = sha1, - filename = filename, - cachefile = cachefile) - return '<<>>\n' % full - -def reposetup(ui, repo): - if not repo.local(): - return - for name, fn in {'download:': download, 'upload:': upload}.iteritems(): - try: - # Hg 0.9.6+ (with f8ad3b76e923): - repo.adddatafilter(name, fn) - except AttributeError: - # Hg 0.9.5: - util.filtertable[name] = fn - -# --- FROM http://odin.himinbi.org/MultipartPostHandler.py --- -# (with some bug fixes!) -import urllib -import urllib2 -import mimetools, mimetypes -import os, stat - -class Callable: - def __init__(self, anycallable): - self.__call__ = anycallable - -# Controls how sequences are uncoded. If true, elements may be given multiple values by -# assigning a sequence. -doseq = 1 - -class MultipartPostHandler(urllib2.BaseHandler): - handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first - - def http_request(self, request): - data = request.get_data() - if data is not None and type(data) != str: - v_files = [] - v_vars = [] - for(key, value) in data.items(): - try: - value.name - v_files.append((key, value)) - except AttributeError: - v_vars.append((key, value)) - - if len(v_files) == 0: - data = urllib.urlencode(v_vars, doseq) - else: - boundary, data = self.multipart_encode(v_vars, v_files) - contenttype = 'multipart/form-data; boundary=%s' % boundary - if(request.has_header('Content-Type') - and request.get_header('Content-Type').find('multipart/form-data') != 0): - print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data') - request.add_unredirected_header('Content-Type', contenttype) - - request.add_data(data) - return request - - def multipart_encode(vars, files, boundary = None, buffer = None): - if boundary is None: - boundary = mimetools.choose_boundary() - if buffer is None: - buffer = '' - for(key, value) in vars: - buffer += '--%s\r\n' % boundary - buffer += 'Content-Disposition: form-data; name="%s"' % key - buffer += '\r\n\r\n' + value + '\r\n' - for(key, fd) in files: - filename = os.path.basename(fd.name) - contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' - buffer += '--%s\r\n' % boundary - buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename) - buffer += 'Content-Type: %s\r\n' % contenttype - fd.seek(0) - buffer += '\r\n' + fd.read() + '\r\n' - buffer += '--%s--\r\n\r\n' % boundary - return boundary, buffer - multipart_encode = Callable(multipart_encode) - - https_request = http_request -# ------------------------------------------------------------ --- a/nbbuild/build.xml +++ a/nbbuild/build.xml @@ -98,13 +98,21 @@ - + + - - + + + + + + + + + @@ -1371,6 +1379,13 @@
+ + + + + + + --- a/nbbuild/default-properties.xml +++ a/nbbuild/default-properties.xml @@ -73,5 +73,8 @@ + + + --- a/nbbuild/templates/projectized.xml +++ a/nbbuild/templates/projectized.xml @@ -71,6 +71,12 @@ + + + + + +