From 0c71e51f27605a2017f58b32678470e48085a298 Mon Sep 17 00:00:00 2001 From: Mitch Chapman Date: Fri, 3 Mar 2006 18:43:39 +0000 Subject: [PATCH] Added basic support for building SCons projects. git-svn-id: http://svn.textmate.org/trunk/Bundles/SCons.tmbundle@2814 dfb7d73b-c2ec-0310-8fea-fb051d288c6d --- Commands/Build.plist | 27 +++++++ LICENSE.BSD | 25 ++++++ Support/bin/scons_gcc_filter.py | 125 ++++++++++++++++++++++++++++++ Support/bin/scons_html_wrapper.py | 73 +++++++++++++++++ info.plist | 14 ++++ 5 files changed, 264 insertions(+) create mode 100644 Commands/Build.plist create mode 100644 LICENSE.BSD create mode 100755 Support/bin/scons_gcc_filter.py create mode 100644 Support/bin/scons_html_wrapper.py create mode 100644 info.plist diff --git a/Commands/Build.plist b/Commands/Build.plist new file mode 100644 index 0000000..52cc3ba --- /dev/null +++ b/Commands/Build.plist @@ -0,0 +1,27 @@ + + + + + beforeRunningCommand + saveModifiedFiles + command + # TM_IGNORE_WARNINGS is a sequence of glob patterns of files in which to ignore +# warnings. Right now it doesn't support escaped ':" -- the separator character. +# export TM_IGNORE_WARNINGS="mitchcode_*.h:*/MitchCode/*" +python -u "${TM_BUNDLE_SUPPORT}/bin/scons_html_wrapper.py" + fallbackInput + word + input + selection + keyEquivalent + @b + name + Build + output + showAsHTML + scope + source.c, source.objc, source.c++, source.objc++, source.python + uuid + C0F342BE-9F5F-4D62-A3C8-D9C95B46F4CD + + diff --git a/LICENSE.BSD b/LICENSE.BSD new file mode 100644 index 0000000..6b7a14e --- /dev/null +++ b/LICENSE.BSD @@ -0,0 +1,25 @@ +This bundle is released under the following BSD license. + +Copyright (C) 2006 by Mitch Chapman +mitch.chapman@mac.com + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Support/bin/scons_gcc_filter.py b/Support/bin/scons_gcc_filter.py new file mode 100755 index 0000000..9c8fd89 --- /dev/null +++ b/Support/bin/scons_gcc_filter.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +"""Filter for scons output, adds TextMate hyperlinks for gcc error msgs.""" + +import re +import sys +import os +import fnmatch +import cgi + +class SConsGCCFilter(object): + stlIgnore = ["iosfwd", "streambuf", "streambuf_iterator.h", "basic_ios.h", + "basic_string.h", "istream", "istream.tcc", + "ostream", "ostream.tcc", + "fstream.tcc", + ] + + def __init__(self, currdir=None): + self._content = "" + self._index = 0 + if currdir is None: + currdir = os.getcwd() + self._currdir = currdir + + self._ignorePatterns = [] + self._initIgnorePatterns() + self._errExpr = re.compile( + r"^(?P" + r"(?P[/A-Za-z0-9_.+]*):" + r"(?P\d+):\s*" + r"(?P(error|warning)):" + r")" + r"(?P.*(\n .*)*)", + re.MULTILINE) + + def _initIgnorePatterns(self): + """Initialize with settings from TM_IGNORE_WARNINGS, if any.""" + value = os.environ.get("TM_IGNORE_WARNINGS") + if value is not None: + # Better hope nobody uses a filename containing ':'. + patterns = value.split(":") + for p in patterns: + self._addIgnorePath(p) + + def _addIgnorePath(self, pattern): + """Ignore (or rather, don't highlight) warnings from files + whose names match the glob pattern.""" + print "Will not highlight warnings in %r
" % cgi.escape(pattern) + self._ignorePatterns.append(pattern) + + def _isIgnoredPath(self, pathname): + for pattern in self._ignorePatterns: + if fnmatch.fnmatch(pathname, pattern): + return True + return False + + def _isIgnoredSTLWarning(self, filename): + # GCC 3.3 -Weffc++ + # produces some warnings because of code generated from + # STL. Uncomment the return statement to pass-through these warnings + #return False + result = ((filename in self.stlIgnore) or + fnmatch.fnmatch(filename, "stl_*.h")) + return result + + def feed(self, newContent): + self._content += newContent + + def _worthHighlighting(self, pathname, isError): + filename = os.path.basename(pathname) + result = (isError or not + (self._isIgnoredPath(pathname) or + self._isIgnoredSTLWarning(filename))) + return result + + def filter(self, consumeAll=False): + resultList = [] + + currdir = self._currdir + content = self._content + search = self._errExpr.search + while True: + match = search(content) + if match is None: + if consumeAll: + resultList.append(content) + self._content = "" + else: + # Error msgs start near beginning of a line, so flush + # all unmatched, complete lines. + iLine = content.rfind("\n") + if (iLine >= 0): + resultList.append(content[:iLine+1]) + content = content[iLine+2:] + # Save the rest for later consumption. + self._content = content + break + else: + resultList.append(content[:match.start()]) + pathname = match.group("pathname") + isError = (match.group("errwarn") == "error") + if self._worthHighlighting(pathname, isError): + msg = match.group("error_msg") + lineNum = match.group("line_num") + errorURL = ("txmt://open?url=file://%s/%s&line=%s" % + (currdir, pathname, lineNum)) + resultList.append('' % errorURL) + resultList.append(match.group("preamble")) + resultList.append('') + resultList.append(msg) + resultList.append("
") + else: + resultList.append(match.group(0)) + + content = content[match.end()+1:] + + result = "".join(resultList).replace("\n", "
\n") + return result + +def main(): + f = SConsGCCFilter() + f.feed(sys.stdin.read()) + print f.filter(consumeAll=True) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Support/bin/scons_html_wrapper.py b/Support/bin/scons_html_wrapper.py new file mode 100644 index 0000000..a213eb1 --- /dev/null +++ b/Support/bin/scons_html_wrapper.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +"""Runs scons and wraps its output as HTML.""" + +import sys +import os +import select +from scons_gcc_filter import SConsGCCFilter +import cgi + +def findSConstructDir(): + """Search upward from the current dir for the top-level SConstruct dir.""" + result = None + d = os.getcwd() + while (d != "/") and not result: + candidate = os.path.join(d, "SConstruct") + if os.path.exists(candidate) and os.path.isfile(candidate): + result = d + d = os.path.dirname(d) + return result or os.getcwd() + +def write(s): + """Write a string to stdout, flushing immediately.""" + if s: + sys.stdout.write(s) + sys.stdout.flush() + +def filterOutput(cmd): + """Run a shell cmd, reformatting output as HTML.""" + theFilter = SConsGCCFilter(findSConstructDir()) + + childIn, childOut, childErr = os.popen3(cmd, 0) + infd = childIn.fileno() + outfd = childOut.fileno() + errfd = childErr.fileno() + inSet = [outfd, errfd] + errSet = [infd, outfd, errfd] + + while True: + ins, outs, errs = select.select(inSet, [], errSet) + if errs: + print "Error descriptors: %s" % repr(errs) + for item in errs: + errSet.remove(item) + if not errSet: + break + for fd in ins: + content = os.read(fd, 65536) + if not content: + inSet.remove(fd) + else: + theFilter.feed(content) + if ins: + write(theFilter.filter()) + if not inSet: + childIn.close() + childOut.close() + childErr.close() + write(theFilter.filter(consumeAll=True)) + break + + +def runSCons(): + """Run SCons in a not-very-flexible way.""" + cmd = "scons -u 2>&1" + print "# Working dir: %s
" % cgi.escape(os.getcwd()) + print "%s
" % cgi.escape(cmd) + filterOutput(cmd) + +def main(): + runSCons() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/info.plist b/info.plist new file mode 100644 index 0000000..bcbf1d0 --- /dev/null +++ b/info.plist @@ -0,0 +1,14 @@ + + + + + name + SCons + ordering + + C0F342BE-9F5F-4D62-A3C8-D9C95B46F4CD + + uuid + 31ACFE4A-592A-41C5-9D23-0C315EC5F7A8 + +