here you go. Sorry for the delay.

On Tue, Jun 10, 2008 at 2:51 PM, Marco Pesenti Gritti <[EMAIL PROTECTED]>
wrote:

> On Mon, Jun 9, 2008 at 7:00 PM, Jameson Chema Quinn
> <[EMAIL PROTECTED]> wrote:
> > Here is the new version of the patch. Tell me if there are any problems
> > applying it cleanly, and I will fix.
>
>
> [EMAIL PROTECTED] sugar-toolkit]$ patch -p1
> </home/marco/Download/bundleMANIFEST.formarco2.patch
> patching file src/sugar/activity/bundlebuilder.py
> Hunk #5 FAILED at 113.
> 1 out of 6 hunks FAILED -- saving rejects to file
> src/sugar/activity/bundlebuilder.py.rej
> patching file src/sugar/bundle/activitybundle.py
> patching file src/sugar/bundle/bundle.py
>
> Marco
> _______________________________________________
> Sugar mailing list
> Sugar@lists.laptop.org
> http://lists.laptop.org/listinfo/sugar
>
From 1ce15e72cfa4a5c7aca081cdeb575cad74905520 Mon Sep 17 00:00:00 2001
From: chema <[EMAIL PROTECTED](none)>
Date: Thu, 12 Jun 2008 17:18:13 -0600
Subject: [PATCH] use MANIFEST. Deprecate bundle_name. fix_manifest().
bundlebuilder.config() cleanup.
---
 src/sugar/activity/bundlebuilder.py |  122 ++++++++++++++++++++++-------------
 src/sugar/bundle/activitybundle.py  |  113 ++++++++++++++++++++++++++++----
 src/sugar/bundle/bundle.py          |   32 ++++++++-
 3 files changed, 207 insertions(+), 60 deletions(-)

diff --git a/src/sugar/activity/bundlebuilder.py b/src/sugar/activity/bundlebuilder.py
index bf21085..d7e4bfd 100644
--- a/src/sugar/activity/bundlebuilder.py
+++ b/src/sugar/activity/bundlebuilder.py
@@ -23,6 +23,8 @@ import subprocess
 import re
 import gettext
 from optparse import OptionParser
+import logging
+from fnmatch import fnmatch
 
 from sugar import env
 from sugar.bundle.activitybundle import ActivityBundle
@@ -31,10 +33,14 @@ def list_files(base_dir, ignore_dirs=None, ignore_files=None):
     result = []
 
     for root, dirs, files in os.walk(base_dir):
+        if ignore_files:
+            for pattern in ignore_files:
+                files = [f for f in files if not fnmatch(f, pattern)]
+                
+        rel_path = root[len(base_dir) + 1:]
         for f in files:
-            if ignore_files and f not in ignore_files:
-                rel_path = root[len(base_dir) + 1:]
-                result.append(os.path.join(rel_path, f))
+            result.append(os.path.join(rel_path, f))
+            
         if ignore_dirs and root == base_dir:
             for ignore in ignore_dirs:
                 if ignore in dirs:
@@ -43,25 +49,27 @@ def list_files(base_dir, ignore_dirs=None, ignore_files=None):
     return result
 
 class Config(object):
-    def __init__(self, bundle_name):
-        self.source_dir = os.getcwd()
-
-        bundle = ActivityBundle(self.source_dir)
-        version = bundle.get_activity_version()
-
-        self.bundle_name = bundle_name
-        self.xo_name = '%s-%d.xo' % (self.bundle_name, version)
-        self.tarball_name = '%s-%d.tar.bz2' % (self.bundle_name, version)
+    def __init__(self, source_dir=None, dist_dir = None, dist_name = None):
+        self.source_dir = source_dir or os.getcwd()
+            
+        self.bundle = bundle = ActivityBundle(self.source_dir)
+        self.version = bundle.get_activity_version()
+        self.activity_name = bundle.get_name()
         self.bundle_id = bundle.get_bundle_id()
-        self.bundle_root_dir = self.bundle_name + '.activity'
-        self.tarball_root_dir = '%s-%d' % (self.bundle_name, version)
+        self.bundle_name = reduce(lambda x, y:x+y, self.activity_name.split())
 
-        info_path = os.path.join(self.source_dir, 'activity', 'activity.info')
-        f = open(info_path,'r')
-        info = f.read()
-        f.close()
-        match = re.search('^name\s*=\s*(.*)$', info, flags = re.MULTILINE)
-        self.activity_name = match.group(1)
+        if dist_dir:
+            self.dist_dir = dist_dir
+        else:
+            self.dist_dir = os.path.join(self.source_dir, 'dist')
+            
+        if dist_name:
+            self.xo_name = self.tar_name = dist_name
+        else:
+            self.xo_name = '%s-%d.xo' % (self.bundle_name, self.version)
+            self.tar_name = '%s-%d.tar.bz2' % (self.bundle_name, self.version)
+        self.bundle_root_dir = self.bundle_name + '.activity'
+        self.tar_root_dir = '%s-%d' % (self.bundle_name, self.version)
 
 class Builder(object):
     def __init__(self, config):
@@ -71,6 +79,10 @@ class Builder(object):
         self.build_locale()
 
     def build_locale(self):
+        if not self.config.bundle.is_dir('po'):
+            logging.warn("Activity lacks a po directory for translations")
+            return
+        
         po_dir = os.path.join(self.config.source_dir, 'po')
 
         for f in os.listdir(po_dir):
@@ -101,54 +113,74 @@ class Builder(object):
 class Packager(object):
     def __init__(self, config):
         self.config = config
-        self.dist_dir = os.path.join(self.config.source_dir, 'dist')
         self.package_path = None
 
-        if not os.path.exists(self.dist_dir):
-            os.mkdir(self.dist_dir)
+        if not os.path.exists(self.config.dist_dir):
+            os.mkdir(self.config.dist_dir)
             
 
 class BuildPackager(Packager):
-    def __init__(self, config):
-        Packager.__init__(self, config)
-        self.build_dir = self.config.source_dir
-
     def get_files(self):
-        return list_files(self.build_dir,
-                          ignore_dirs=['po', 'dist', '.git'],
-                          ignore_files=['.gitignore'])
+        return self.config.bundle.get_files()
+    
+    def _list_useful_files(self):
+        ignore_dirs = ['dist', '.git'],
+        ignore_files = ['.gitignore', 'MANIFEST', '*.pyc', '*~', '*.bak']
+        
+        return list_files(self.config.source_dir, ignore_dirs, ignore_files)
+        
+    def fix_manifest(self):
+        manifest = self.config.bundle.manifest
+        
+        allfiles = self._list_useful_files()
+        
+        for path in allfiles:
+            if path not in manifest:
+                manifest.append(path)
+        #note: duplicate/invalid entries already gone from bundle.manifest
+        
+        f = open(os.path.join(self.config.source_dir, "MANIFEST"), "wb")
+        for line in manifest:
+            f.write(line + "\n")
 
 class XOPackager(BuildPackager):
     def __init__(self, config):
         BuildPackager.__init__(self, config)
-        self.package_path = os.path.join(self.dist_dir, self.config.xo_name)
+        self.package_path = os.path.join(self.config.dist_dir,
+                                         self.config.xo_name)
 
     def package(self):
         bundle_zip = zipfile.ZipFile(self.package_path, 'w',
                                      zipfile.ZIP_DEFLATED)
         
         for f in self.get_files():
-            bundle_zip.write(os.path.join(self.build_dir, f),
+            bundle_zip.write(os.path.join(self.config.source_dir, f),
                              os.path.join(self.config.bundle_root_dir, f))
 
         bundle_zip.close()
 
-class SourcePackager(Packager):
+class SourcePackager(BuildPackager):
     def __init__(self, config):
-        Packager.__init__(self, config)
-        self.package_path = os.path.join(self.dist_dir,
-                                         self.config.tarball_name)
+        BuildPackager.__init__(self, config)
+        self.package_path = os.path.join(self.config.dist_dir,
+                                         self.config.tar_name)
 
     def get_files(self):
-        return list_files(self.config.source_dir,
-                          ignore_dirs=['locale', 'dist', '.git'],
-                          ignore_files=['.gitignore'])
+        #list files in git
+        git_ls = subprocess.Popen('git-ls-files', stdout=subprocess.PIPE, 
+                                  cwd=self.config.source_dir)
+        if git_ls.wait():
+            #fall back to filtered list
+            return self._list_useful_files()
+        paths = git_ls.stdout.readlines()
+        
+        return [path.strip() for path in paths if not path.startswith('.')]
 
     def package(self):
-        tar = tarfile.open(self.package_path, "w:bz2")
+        tar = tarfile.open(self.package_path, "w")
         for f in self.get_files():
             tar.add(os.path.join(self.config.source_dir, f),
-                    os.path.join(self.config.tarball_root_dir, f))
+                    os.path.join(self.config.tar_root_dir, f))
         tar.close()
 
 def cmd_help(config, options, args):
@@ -168,7 +200,7 @@ def cmd_dev(config, options, args):
     bundle_path = env.get_user_activities_path()
     if not os.path.isdir(bundle_path):
         os.mkdir(bundle_path)
-    bundle_path = os.path.join(bundle_path, config.bundle_root_dir)
+    bundle_path = os.path.join(bundle_path, config.bundle_top_dir)
     try:
         os.symlink(config.source_dir, bundle_path)
     except OSError:
@@ -331,11 +363,13 @@ def cmd_build(config, options, args):
     builder = Builder(config)
     builder.build()
 
-def start(bundle_name):
+def start(bundle_name=None):
+    if bundle_name:
+        logging.warn("bundle_name deprecated, now comes from activity.info")
     parser = OptionParser()
     (options, args) = parser.parse_args()
 
-    config = Config(bundle_name)
+    config = Config()
 
     try:
         globals()['cmd_' + args[0]](config, options, args[1:])
diff --git a/src/sugar/bundle/activitybundle.py b/src/sugar/bundle/activitybundle.py
index db30555..8dba9ba 100644
--- a/src/sugar/bundle/activitybundle.py
+++ b/src/sugar/bundle/activitybundle.py
@@ -56,7 +56,7 @@ class ActivityBundle(Bundle):
         self._show_launcher = True
         self._activity_version = 0
 
-        info_file = self._get_file('activity/activity.info')
+        info_file = self.get_file('activity/activity.info')
         if info_file is None:
             raise MalformedBundleException('No activity.info file')
         self._parse_info(info_file)
@@ -65,6 +65,62 @@ class ActivityBundle(Bundle):
         if linfo_file:
             self._parse_linfo(linfo_file)
 
+        self.manifest = None #This should be replaced by following function
+        self.read_manifest()
+
+    def _raw_manifest(self):
+        f = self.get_file("MANIFEST")
+        if not f:
+            logging.warning("Activity directory lacks a MANIFEST file.")
+            return []
+        
+        ret = [line.strip() for line in f.readlines()] 
+        f.close()
+        return ret
+        
+    def read_manifest(self):
+        """read_manifest: sets self.manifest to list of lines in MANIFEST, 
+        with invalid lines replaced by empty lines.
+        
+        Since absolute order carries information on file history, it should 
+        be preserved.
+        For instance, when renaming a file, you should leave the new name 
+        on the same line as the old one.
+        """
+        lines = self._raw_manifest()
+        for num, line in enumerate(lines):
+            if line:
+                #remove duplicates
+                if line in lines[0:num]:
+                    lines[num] = ""
+                    logging.warning("Bundle %s: duplicate entry in MANIFEST: %s"
+                                    %(self._name,line))
+                    continue
+                
+                #remove MANIFEST
+                if line == "MANIFEST":
+                    lines[num] = ""
+                    logging.warning("Bundle %s: MANIFEST includes itself: %s"
+                                    %(self._name,line))
+                    
+                #check files
+                if not self.is_file(line):
+                    lines[num] = ""
+                    logging.warning("Bundle %s: "
+                                    "invalid entry in MANIFEST: %s"
+                                    %(self._name,line))
+                        
+        #remove trailing newlines - unlike internal newlines, 
+        #  they do not help keep absolute position
+        while lines and lines[-1] == "":
+            lines = lines[:-1]
+        self.manifest = lines
+    
+    def get_files(self, manifest = None):
+        manifestpath = ["MANIFEST"] if self.is_file("MANIFEST") else []
+        return manifestpath + [line for line in (manifest or self.manifest) 
+                               if line]
+      
     def _parse_info(self, info_file):
         cp = ConfigParser()
         cp.readfp(info_file)
@@ -123,12 +179,12 @@ class ActivityBundle(Bundle):
             return None
 
         linfo_path = os.path.join('locale', lang, 'activity.linfo')
-        linfo_file = self._get_file(linfo_path)
+        linfo_file = self.get_file(linfo_path)
         if linfo_file is not None:
             return linfo_file
 
         linfo_path = os.path.join('locale', lang[:2], 'activity.linfo')
-        linfo_file = self._get_file(linfo_path)
+        linfo_file = self.get_file(linfo_path)
         if linfo_file is not None:
             return linfo_file
 
@@ -180,7 +236,7 @@ class ActivityBundle(Bundle):
         if self._unpacked:
             return os.path.join(self._path, icon_path)
         else:
-            icon_data = self._get_file(icon_path).read()
+            icon_data = self.get_file(icon_path).read()
             temp_file, temp_file_path = tempfile.mkstemp(self._icon)
             os.write(temp_file, icon_data)
             os.close(temp_file)
@@ -220,17 +276,38 @@ class ActivityBundle(Bundle):
             return True
         else:
             return False
-
-    def install(self):
-        activities_path = env.get_user_activities_path()
-        act = activity.get_registry().get_activity(self._bundle_id)
-        if act is not None and act.path.startswith(activities_path):
-            raise AlreadyInstalledException
-
-        install_dir = env.get_user_activities_path()
+    
+    def unpack(self, install_dir, strict_manifest=False):
         self._unzip(install_dir)
 
         install_path = os.path.join(install_dir, self._zip_root_dir)
+        
+        #list installed files
+        manifestfiles = self.get_files(self._raw_manifest())
+        paths  = []
+        for root, dirs, files in os.walk(install_path):
+            rel_path = root[len(install_path) + 1:]
+            for f in files:
+                paths.append(os.path.join(rel_path, f))
+                
+        #check list against the MANIFEST
+        for path in paths:
+            if path in manifestfiles:
+                manifestfiles.remove(path)
+            elif path != "MANIFEST":
+                logging.warning("Bundle %s: %s not in MANIFEST"%
+                                (self._name,path))
+                if strict_manifest:
+                    os.remove(os.path.join(install_path, path))
+                    
+        #Is anything in MANIFEST left over after accounting for all files?
+        if manifestfiles:
+            err = ("Bundle %s: files in MANIFEST not included: %s"%
+                   (self._name,str(manifestfiles)))
+            if strict_manifest:
+                raise MalformedBundleException(err)
+            else:
+                logging.warning(err)
 
         xdg_data_home = os.getenv('XDG_DATA_HOME',
                                   os.path.expanduser('~/.local/share'))
@@ -267,11 +344,21 @@ class ActivityBundle(Bundle):
                     os.symlink(info_file,
                                os.path.join(installed_icons_dir,
                                             os.path.basename(info_file)))
+        return install_path
 
+    def install(self):
+        activities_path = env.get_user_activities_path()
+        act = activity.get_registry().get_activity(self._bundle_id)
+        if act is not None and act.path.startswith(activities_path):
+            raise AlreadyInstalledException
+
+        install_dir = env.get_user_activities_path()
+        install_path = self.unpack(install_dir)
+        
         if not activity.get_registry().add_bundle(install_path):
             raise RegistrationException
 
-    def uninstall(self, force=False):
+    def uninstall(self, force=False):        
         if self._unpacked:
             install_path = self._path
         else:
diff --git a/src/sugar/bundle/bundle.py b/src/sugar/bundle/bundle.py
index 47d08b2..90b511b 100644
--- a/src/sugar/bundle/bundle.py
+++ b/src/sugar/bundle/bundle.py
@@ -96,13 +96,15 @@ class Bundle:
                     'All files in the bundle must be inside a single ' +
                     'top-level directory')
 
-    def _get_file(self, filename):
+    def get_file(self, filename):
         f = None
 
         if self._unpacked:
             path = os.path.join(self._path, filename)
-            if os.path.isfile(path):
-                f = open(path)
+            try:
+                f = open(path,"rb")
+            except IOError:
+                return None
         else:
             zip_file = zipfile.ZipFile(self._path)
             path = os.path.join(self._zip_root_dir, filename)
@@ -114,6 +116,30 @@ class Bundle:
             zip_file.close()
 
         return f
+    
+    def is_dir(self, filename):
+        if self._unpacked:
+            path = os.path.join(self._path, filename)
+            return os.path.isdir(path)
+        else:
+            return True #zip files contain all dirs you care about!
+            
+    def is_file(self, filename):
+        if self._unpacked:
+            path = os.path.join(self._path, filename)
+            return os.path.isfile(path)
+        else:
+            zip_file = zipfile.ZipFile(self._path)
+            path = os.path.join(self._zip_root_dir, filename)
+            try:
+                zip_file.getinfo(path)
+                #no exception above, so:
+                return True
+            except KeyError:
+                return False
+            finally:
+                zip_file.close()
+                
 
     def get_path(self):
         """Get the bundle path."""
-- 
1.5.2.5

_______________________________________________
Sugar mailing list
Sugar@lists.laptop.org
http://lists.laptop.org/listinfo/sugar

Reply via email to