[117407] trunk/base

jmr at macports.org jmr at macports.org
Tue Feb 25 10:28:14 PST 2014


Revision: 117407
          https://trac.macports.org/changeset/117407
Author:   jmr at macports.org
Date:     2014-02-25 10:28:14 -0800 (Tue, 25 Feb 2014)
Log Message:
-----------
store portgroups in the registry, so they can always be used by the portfiles, especially when a portgroup only exists in a non-default source.
move portfiles out of the db and into the filesystem, and deduplicate them (for when the same version is installed with different variants)

Modified Paths:
--------------
    trunk/base/Makefile.in
    trunk/base/portmgr/dmg/postflight
    trunk/base/src/cregistry/Makefile
    trunk/base/src/cregistry/entry.c
    trunk/base/src/cregistry/entry.h
    trunk/base/src/cregistry/registry.c
    trunk/base/src/cregistry/registry.h
    trunk/base/src/cregistry/sql.c
    trunk/base/src/macports1.0/macports.tcl
    trunk/base/src/macports1.0/tests/macports.test
    trunk/base/src/pextlib1.0/tracelib.c
    trunk/base/src/port1.0/portinstall.tcl
    trunk/base/src/port1.0/portutil.tcl
    trunk/base/src/registry2.0/Makefile
    trunk/base/src/registry2.0/entryobj.c
    trunk/base/src/registry2.0/portuninstall.tcl
    trunk/base/src/registry2.0/registry.c
    trunk/base/src/registry2.0/registry.h
    trunk/base/src/registry2.0/registry_util.tcl
    trunk/base/src/registry2.0/util.c
    trunk/base/src/registry2.0/util.h

Added Paths:
-----------
    trunk/base/src/cregistry/portgroup.c
    trunk/base/src/cregistry/portgroup.h
    trunk/base/src/dedup_portfiles.tcl
    trunk/base/src/registry2.0/portgroup.c
    trunk/base/src/registry2.0/portgroup.h
    trunk/base/src/registry2.0/portgroupobj.c
    trunk/base/src/registry2.0/portgroupobj.h

Modified: trunk/base/Makefile.in
===================================================================
--- trunk/base/Makefile.in	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/Makefile.in	2014-02-25 18:28:14 UTC (rev 117407)
@@ -94,6 +94,7 @@
 	$(TCLSH) src/upgrade_sources_conf_default.tcl "${prefix}"
 # Convert image directories (and direct mode installs) to image archives
 	$(TCLSH) src/images_to_archives.tcl "${macports_tcl_dir}"
+	$(TCLSH) src/dedup_portfiles.tcl "${macports_tcl_dir}"
 endif
 ifndef SELFUPDATING
 	@echo ""; echo "Congratulations, you have successfully installed the MacPorts system. To get the Portfiles and update the system, add ${prefix}/bin to your PATH and run:"; echo ""

Modified: trunk/base/portmgr/dmg/postflight
===================================================================
--- trunk/base/portmgr/dmg/postflight	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/portmgr/dmg/postflight	2014-02-25 18:28:14 UTC (rev 117407)
@@ -74,6 +74,7 @@
     # Convert image directories (and direct mode installs) to image archives
     echo "Updating port image format..."
     ${TCLSH} ${SCRIPT_DIR}/images_to_archives.tcl ${MACPORTS_TCL_DIR}
+    ${TCLSH} ${SCRIPT_DIR}/dedup_portfiles.tcl ${MACPORTS_TCL_DIR}
 
     echo "Synchronizing the MacPorts installation with the project's rsync server..."
     if ! ${BINPATH}/port -v selfupdate; then 

Modified: trunk/base/src/cregistry/Makefile
===================================================================
--- trunk/base/src/cregistry/Makefile	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/Makefile	2014-02-25 18:28:14 UTC (rev 117407)
@@ -1,6 +1,6 @@
 # $Id$
 
-OBJS = registry.o entry.o sql.o vercomp.o util.o file.o
+OBJS = registry.o entry.o sql.o vercomp.o util.o file.o portgroup.o
 STLIB_NAME = cregistry.a
 RANLIB = ranlib
 

Modified: trunk/base/src/cregistry/entry.c
===================================================================
--- trunk/base/src/cregistry/entry.c	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/entry.c	2014-02-25 18:28:14 UTC (rev 117407)
@@ -2,7 +2,7 @@
  * entry.c
  * $Id$
  *
- * Copyright (c) 2010-2011 The MacPorts Project
+ * Copyright (c) 2010-2011, 2014 The MacPorts Project
  * Copyright (c) 2007 Chris Pickel <sfiera at macports.org>
  * All rights reserved.
  *
@@ -31,6 +31,7 @@
 #include <config.h>
 #endif
 
+#include "portgroup.h"
 #include "entry.h"
 #include "registry.h"
 #include "sql.h"
@@ -246,9 +247,11 @@
     sqlite3_stmt* ports = NULL;
     sqlite3_stmt* files = NULL;
     sqlite3_stmt* dependencies = NULL;
+    sqlite3_stmt* portgroups = NULL;
     char* ports_query = "DELETE FROM registry.ports WHERE id=?";
     char* files_query = "DELETE FROM registry.files WHERE id=?";
     char* dependencies_query = "DELETE FROM registry.dependencies WHERE id=?";
+    char* portgroups_query = "DELETE FROM registry.portgroups WHERE id=?";
     if ((sqlite3_prepare_v2(reg->db, ports_query, -1, &ports, NULL) == SQLITE_OK)
             && (sqlite3_bind_int64(ports, 1, entry->id) == SQLITE_OK)
             && (sqlite3_prepare_v2(reg->db, files_query, -1, &files, NULL)
@@ -256,7 +259,10 @@
             && (sqlite3_bind_int64(files, 1, entry->id) == SQLITE_OK)
             && (sqlite3_prepare_v2(reg->db, dependencies_query, -1, &dependencies,
                     NULL) == SQLITE_OK)
-            && (sqlite3_bind_int64(dependencies, 1, entry->id) == SQLITE_OK)) {
+            && (sqlite3_bind_int64(dependencies, 1, entry->id) == SQLITE_OK)
+            && (sqlite3_prepare_v2(reg->db, portgroups_query, -1, &portgroups,
+                    NULL) == SQLITE_OK)
+            && (sqlite3_bind_int64(portgroups, 1, entry->id) == SQLITE_OK)) {
         int r;
         do {
             r = sqlite3_step(ports);
@@ -271,7 +277,20 @@
                                         r = sqlite3_step(dependencies);
                                         switch (r) {
                                             case SQLITE_DONE:
-                                                result = 1;
+                                                do {
+                                                    r = sqlite3_step(portgroups);
+                                                    switch (r) {
+                                                        case SQLITE_DONE:
+                                                            result = 1;
+                                                            break;
+                                                        case SQLITE_BUSY:
+                                                            break;
+                                                        case SQLITE_ERROR:
+                                                            reg_sqlite_error(reg->db,
+                                                                    errPtr, NULL);
+                                                            break;
+                                                    }
+                                                } while (r == SQLITE_BUSY);
                                                 break;
                                             case SQLITE_BUSY:
                                                 break;
@@ -315,6 +334,9 @@
     if (dependencies) {
         sqlite3_finalize(dependencies);
     }
+    if (portgroups) {
+        sqlite3_finalize(portgroups);
+    }
     return result;
 }
 
@@ -670,6 +692,73 @@
 }
 
 /**
+ * Associates a portgroup with given port.
+ *
+ * @param [in] entry      the entry to map the portgroup to
+ * @param [in] name       the portgroup name (e.g. "muniversal")
+ * @param [in] version    the portgroup version (e.g. "1.0")
+ * @param [in] sha256     the sha256 hash of the portgroup file
+ * @param [in] size       the size of the portgroup file in bytes
+ * @param [out] errPtr    on error, a description of the error that occurred
+ * @return                true if success; false if failure
+ */
+int reg_entry_addgroup(reg_entry* entry, char* name, char *version,
+        char *sha256, sqlite_int64 size, reg_error* errPtr) {
+    reg_registry* reg = entry->reg;
+    int result = 1;
+    sqlite3_stmt* stmt = NULL;
+    char* insert = "INSERT INTO registry.portgroups (id, name, version, size, sha256) "
+        "VALUES (?, ?, ?, ?, ?)";
+    if ((sqlite3_prepare_v2(reg->db, insert, -1, &stmt, NULL) == SQLITE_OK)
+            && (sqlite3_bind_int64(stmt, 1, entry->id) == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC) == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 3, version, -1, SQLITE_STATIC) == SQLITE_OK)
+            && (sqlite3_bind_int64(stmt, 4, size) == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 5, sha256, -1, SQLITE_STATIC) == SQLITE_OK)) {
+        int r;
+        do {
+            r = sqlite3_step(stmt);
+            switch (r) {
+                case SQLITE_DONE:
+                    sqlite3_reset(stmt);
+                    break;
+                case SQLITE_BUSY:
+                    break;
+                default:
+                    reg_sqlite_error(reg->db, errPtr, insert);
+                    result = 0;
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg->db, errPtr, insert);
+        result = 0;
+    }
+    if (stmt) {
+        sqlite3_finalize(stmt);
+    }
+    return result;
+}
+
+/**
+ * Gets a list of portgroups that are used by this port.
+ *
+ * @param [in] entry       a port
+ * @param [out] portgroups a list of portgroups used by the given port
+ * @param [out] errPtr     on error, a description of the error that occurred
+ * @return                 true if success; false if failure
+ */
+int reg_entry_getgroups(reg_entry* entry, reg_portgroup*** portgroups, reg_error* errPtr) {
+    reg_registry* reg = entry->reg;
+    char* query = sqlite3_mprintf("SELECT ROWID FROM portgroups "
+            "WHERE id=%lld",
+            entry->id);
+    int result = reg_all_portgroups(reg, query, -1, portgroups, errPtr);
+    sqlite3_free(query);
+    return result;
+}
+
+/**
  * Maps files to the given port in the filemap. The list of files must not
  * contain files that are already mapped to the given port.
  *

Modified: trunk/base/src/cregistry/entry.h
===================================================================
--- trunk/base/src/cregistry/entry.h	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/entry.h	2014-02-25 18:28:14 UTC (rev 117407)
@@ -71,6 +71,11 @@
 int reg_entry_propset(reg_entry* entry, char* key, char* value,
         reg_error* errPtr);
 
+int reg_entry_addgroup(reg_entry* entry, char* name, char *version,
+        char *sha256, sqlite_int64 size, reg_error* errPtr);
+int reg_entry_getgroups(reg_entry* entry, reg_portgroup*** portgroups,
+        reg_error* errPtr);
+
 int reg_entry_map(reg_entry* entry, char** files, int file_count,
         reg_error* errPtr);
 int reg_entry_unmap(reg_entry* entry, char** files, int file_count,

Added: trunk/base/src/cregistry/portgroup.c
===================================================================
--- trunk/base/src/cregistry/portgroup.c	                        (rev 0)
+++ trunk/base/src/cregistry/portgroup.c	2014-02-25 18:28:14 UTC (rev 117407)
@@ -0,0 +1,318 @@
+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "portgroup.h"
+#include "util.h"
+#include "registry.h"
+#include "sql.h"
+
+#include <sqlite3.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Converts a `sqlite3_stmt` into a `reg_portgroup`. The first column of the stmt's
+ * row must be the id of a portgroup; the second either `SQLITE_NULL` or the
+ * address of the entry in memory.
+ *
+ * @param [in] userdata sqlite3 database
+ * @param [out] portgroup   portgroup described by `stmt`
+ * @param [in] stmt     `sqlite3_stmt` with appropriate columns
+ * @param [out] errPtr  unused
+ * @return              true if success; false if failure
+ */
+static int reg_stmt_to_portgroup(void* userdata, void** portgroup, void* stmt,
+        void* calldata UNUSED, reg_error* errPtr UNUSED) {
+    int is_new;
+    reg_registry* reg = (reg_registry*)userdata;
+    sqlite_int64 id = sqlite3_column_int64(stmt, 0);
+    Tcl_HashEntry* hash = Tcl_CreateHashEntry(&reg->open_portgroups,
+            (const char*)&id, &is_new);
+    if (is_new) {
+        reg_portgroup* p = malloc(sizeof(reg_portgroup));
+        if (!p) {
+            return 0;
+        }
+        p->reg = reg;
+        p->id = id;
+        p->proc = NULL;
+        *portgroup = p;
+        Tcl_SetHashValue(hash, p);
+    } else {
+        *portgroup = Tcl_GetHashValue(hash);
+    }
+    return 1;
+}
+
+/**
+ * Type-safe version of `reg_all_objects` for `reg_portgroup`.
+ *
+ * @param [in] reg       registry to select entries from
+ * @param [in] query     the select query to execute
+ * @param [in] query_len length of the query (or -1 for automatic)
+ * @param [out] objects  the portgroups selected
+ * @param [out] errPtr   on error, a description of the error that occurred
+ * @return               the number of entries if success; negative if failure
+ */
+int reg_all_portgroups(reg_registry* reg, char* query, int query_len,
+        reg_portgroup*** objects, reg_error* errPtr) {
+    int lower_bound = 0;
+    return reg_all_objects(reg, query, query_len, (void***)objects,
+            reg_stmt_to_portgroup, &lower_bound, NULL, errPtr);
+}
+
+/**
+ * Searches the registry for portgroups for which each key's value is equal to the
+ * given value. To find all portgroups, pass a key_count of 0.
+ *
+ * Bad keys should cause sqlite3 errors but not permit SQL injection attacks.
+ * Pass it good keys anyway.
+ *
+ * @param [in] reg       registry to search in
+ * @param [in] keys      a list of keys to search by
+ * @param [in] vals      a list of values to search by, matching keys
+ * @param [in] strats    a list of strategies to use when searching
+ * @param [in] key_count the number of key/value pairs passed
+ * @param [out] portgroups    a list of matching portgroups
+ * @param [out] errPtr   on error, a description of the error that occurred
+ * @return               the number of entries if success; false if failure
+ */
+int reg_portgroup_search(reg_registry* reg, char** keys, char** vals, int* strats,
+        int key_count, reg_portgroup*** portgroups, reg_error* errPtr) {
+    int i;
+    char* kwd = " WHERE ";
+    char* query;
+    size_t query_len, query_space;
+    int result;
+
+    /* build the query */
+    query = strdup("SELECT ROWID FROM registry.portgroups");
+    if (!query) {
+        return -1;
+    }
+    query_len = query_space = strlen(query);
+
+    for (i = 0; i < key_count; i++) {
+        char* op;
+        char* cond;
+
+        /* get the strategy */
+        if ((op = reg_strategy_op(strats[i], errPtr)) == NULL) {
+            free(query);
+            return -1;
+        }
+
+        cond = sqlite3_mprintf(op, keys[i], vals[i]);
+        if (!cond || !reg_strcat(&query, &query_len, &query_space, kwd)
+            || !reg_strcat(&query, &query_len, &query_space, cond)) {
+            free(query);
+            return -1;
+        }
+        sqlite3_free(cond);
+        kwd = " AND ";
+    }
+
+    /* do the query */
+    result = reg_all_portgroups(reg, query, -1, portgroups, errPtr);
+    free(query);
+    return result;
+}
+
+/**
+ * Gets a named property of a portgroup. That property can be set using
+ * `reg_portgroup_propset`. The property named must be one that exists in the table
+ * and must not be one with internal meaning such as `id` or `state`.
+ *
+ * @param [in] portgroup   portgroup to get property from
+ * @param [in] key     property to get
+ * @param [out] value  the value of the property
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return             true if success; false if failure
+ */
+int reg_portgroup_propget(reg_portgroup* portgroup, char* key, char** value,
+        reg_error* errPtr) {
+    reg_registry* reg = portgroup->reg;
+    int result = 0;
+    sqlite3_stmt* stmt = NULL;
+    char* query;
+    const char *text;
+    query = sqlite3_mprintf("SELECT %q FROM registry.portgroups WHERE ROWID=%lld", key,
+            portgroup->id);
+    if (sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK) {
+        int r;
+        do {
+            r = sqlite3_step(stmt);
+            switch (r) {
+                case SQLITE_ROW:
+                    text = (const char*)sqlite3_column_text(stmt, 0);
+                    if (text) {
+                        *value = strdup(text);
+                        result = 1;
+                    } else {
+                        reg_sqlite_error(reg->db, errPtr, query);
+                    }
+                    break;
+                case SQLITE_DONE:
+                    errPtr->code = REG_INVALID;
+                    errPtr->description = "an invalid portgroup was passed";
+                    errPtr->free = NULL;
+                    break;
+                case SQLITE_BUSY:
+                    continue;
+                default:
+                    reg_sqlite_error(reg->db, errPtr, query);
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg->db, errPtr, query);
+    }
+    if (stmt) {
+        sqlite3_finalize(stmt);
+    }
+    sqlite3_free(query);
+    return result;
+}
+
+/**
+ * Sets a named property of an portgroup. That property can be later retrieved using
+ * `reg_portgroup_propget`. The property named must be one that exists in the table
+ * and must not be one with internal meaning such as `id` or `state`. If `name`,
+ * `epoch`, `version`, `revision`, or `variants` is set, it could trigger a
+ * conflict if another port with the same combination of values for those
+ * columns exists.
+ *
+ * @param [in] portgroup   portgroup to set property for
+ * @param [in] key     property to set
+ * @param [in] value   the desired value of the property
+ * @param [out] errPtr on error, a description of the error that occurred
+ * @return             true if success; false if failure
+ */
+int reg_portgroup_propset(reg_portgroup* portgroup, char* key, char* value,
+        reg_error* errPtr) {
+    reg_registry* reg = portgroup->reg;
+    int result = 0;
+    sqlite3_stmt* stmt = NULL;
+    char* query;
+    query = sqlite3_mprintf("UPDATE registry.ports SET %q = '%q' WHERE ROWID=%lld",
+            key, value, portgroup->id);
+    if (sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK) {
+        int r;
+        do {
+            r = sqlite3_step(stmt);
+            switch (r) {
+                case SQLITE_DONE:
+                    result = 1;
+                    break;
+                case SQLITE_BUSY:
+                    break;
+                default:
+                    if (sqlite3_reset(stmt) == SQLITE_CONSTRAINT) {
+                        errPtr->code = REG_CONSTRAINT;
+                        errPtr->description = "a constraint was disobeyed";
+                        errPtr->free = NULL;
+                    } else {
+                        reg_sqlite_error(reg->db, errPtr, query);
+                    }
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg->db, errPtr, query);
+    }
+    if (stmt) {
+        sqlite3_finalize(stmt);
+    }
+    sqlite3_free(query);
+    return result;
+}
+
+/**
+ * Opens an existing portgroup in the registry.
+ *
+ * @param [in] reg      registry to open portgroup in
+ * @param [in] id       id of entry referencing portgroup
+ * @param [in] name     name of portgroup
+ * @param [in] version  version of portgroup
+ * @param [in] size     size of portgroup
+ * @param [in] sha256   sha256 of portgroup
+ * @param [out] errPtr  on error, a description of the error that occurred
+ * @return              the portgroup if success; NULL if failure
+ */
+reg_portgroup* reg_portgroup_open(reg_registry* reg, char *id, char* name, char* version,
+        char* size, char* sha256, reg_error* errPtr) {
+    sqlite3_stmt* stmt = NULL;
+    reg_portgroup* portgroup = NULL;
+    int lower_bound = 0;
+    char* query;
+    query = "SELECT ROWID FROM registry.portgroups WHERE id=? AND name=? AND version=? "
+        "AND size=? AND sha256=?";
+    if ((sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 1, id, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 3, version, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 4, size, -1, SQLITE_STATIC)
+                == SQLITE_OK)
+            && (sqlite3_bind_text(stmt, 5, sha256, -1, SQLITE_STATIC)
+                == SQLITE_OK)) {
+        int r;
+        do {
+            r = sqlite3_step(stmt);
+            switch (r) {
+                case SQLITE_ROW:
+                    reg_stmt_to_portgroup(reg, (void**)&portgroup, stmt, &lower_bound, errPtr);
+                    break;
+                case SQLITE_DONE:
+                    errPtr->code = REG_NOT_FOUND;
+                    errPtr->description = sqlite3_mprintf("no matching portgroup found for: " \
+                            "id=%s, name=%s, version=%s, size=%s, sha256=%s", \
+                            id, name, version, size, sha256);
+                    errPtr->free = (reg_error_destructor*) sqlite3_free;
+                    break;
+                case SQLITE_BUSY:
+                    continue;
+                default:
+                    reg_sqlite_error(reg->db, errPtr, query);
+                    break;
+            }
+        } while (r == SQLITE_BUSY);
+    } else {
+        reg_sqlite_error(reg->db, errPtr, query);
+    }
+    if (stmt) {
+        sqlite3_finalize(stmt);
+    }
+    return portgroup;
+}

Added: trunk/base/src/cregistry/portgroup.h
===================================================================
--- trunk/base/src/cregistry/portgroup.h	                        (rev 0)
+++ trunk/base/src/cregistry/portgroup.h	2014-02-25 18:28:14 UTC (rev 117407)
@@ -0,0 +1,56 @@
+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ */
+#ifndef _CPORTGROUP_H
+#define _CPORTGROUP_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "registry.h"
+
+#include <sqlite3.h>
+
+typedef struct {
+    sqlite_int64 id; /* rowid in the database */
+    reg_registry* reg; /* associated registry */
+    char* proc; /* name of Tcl proc, if using Tcl */
+} reg_portgroup;
+
+int reg_portgroup_search(reg_registry* reg, char** keys, char** vals, int* strats,
+        int key_count, reg_portgroup*** portgroups, reg_error* errPtr);
+int reg_all_portgroups(reg_registry* reg, char* query, int query_len,
+        reg_portgroup*** objects, reg_error* errPtr);
+reg_portgroup* reg_portgroup_open(reg_registry* reg, char *id, char* name, char* version,
+        char* size, char* sha256, reg_error* errPtr);
+int reg_portgroup_propget(reg_portgroup* portgroup, char* key, char** value,
+        reg_error* errPtr);
+int reg_portgroup_propset(reg_portgroup* portgroup, char* key, char* value,
+        reg_error* errPtr);
+
+#endif /* _CPORTGROUP_H */

Modified: trunk/base/src/cregistry/registry.c
===================================================================
--- trunk/base/src/cregistry/registry.c	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/registry.c	2014-02-25 18:28:14 UTC (rev 117407)
@@ -4,7 +4,7 @@
  * vim:expandtab:tw=80
  *
  * Copyright (c) 2007 Chris Pickel <sfiera at macports.org>
- * Copyright (c) 2012 The MacPorts Project
+ * Copyright (c) 2012, 2014 The MacPorts Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,6 +32,7 @@
 #include <config.h>
 #endif
 
+#include "portgroup.h"
 #include "entry.h"
 #include "file.h"
 #include "sql.h"
@@ -208,6 +209,8 @@
                                     sizeof(sqlite_int64)/sizeof(int));
                             Tcl_InitHashTable(&reg->open_files,
                                     TCL_STRING_KEYS);
+                            Tcl_InitHashTable(&reg->open_portgroups,
+                                    sizeof(sqlite_int64)/sizeof(int));
                             reg->status |= reg_attached;
                             result = 1;
                         }

Modified: trunk/base/src/cregistry/registry.h
===================================================================
--- trunk/base/src/cregistry/registry.h	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/registry.h	2014-02-25 18:28:14 UTC (rev 117407)
@@ -4,7 +4,7 @@
  * $Id$
  *
  * Copyright (c) 2007 Chris Pickel <sfiera at macports.org>
- * Copyright (c) 2012 The MacPorts Project
+ * Copyright (c) 2012, 2014 The MacPorts Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -73,6 +73,7 @@
     int status;
     Tcl_HashTable open_entries;
     Tcl_HashTable open_files;
+    Tcl_HashTable open_portgroups;
 } reg_registry;
 
 int reg_open(reg_registry** regPtr, reg_error* errPtr);

Modified: trunk/base/src/cregistry/sql.c
===================================================================
--- trunk/base/src/cregistry/sql.c	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/cregistry/sql.c	2014-02-25 18:28:14 UTC (rev 117407)
@@ -3,7 +3,7 @@
  * $Id$
  *
  * Copyright (c) 2007 Chris Pickel <sfiera at macports.org>
- * Copyright (c) 2012 The MacPorts Project
+ * Copyright (c) 2012, 2014 The MacPorts Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -129,13 +129,13 @@
 
         /* metadata table */
         "CREATE TABLE registry.metadata (key UNIQUE, value)",
-        "INSERT INTO registry.metadata (key, value) VALUES ('version', 1.100)",
+        "INSERT INTO registry.metadata (key, value) VALUES ('version', 1.200)",
         "INSERT INTO registry.metadata (key, value) VALUES ('created', strftime('%s', 'now'))",
 
         /* ports table */
         "CREATE TABLE registry.ports ("
-            "id INTEGER PRIMARY KEY AUTOINCREMENT, "
-            "name TEXT COLLATE NOCASE, portfile CLOB, url TEXT, "
+            "id INTEGER PRIMARY KEY, "
+            "name TEXT COLLATE NOCASE, portfile TEXT, url TEXT, "
             "location TEXT, epoch INTEGER, version TEXT COLLATE VERSION, "
             "revision INTEGER, variants TEXT, negated_variants TEXT, "
             "state TEXT, date DATETIME, installtype TEXT, archs TEXT, "
@@ -163,6 +163,11 @@
         "FOREIGN KEY(id) REFERENCES ports(id))",
         "CREATE INDEX registry.dep_name ON dependencies (name)",
 
+        /* portgroups table */
+        "CREATE TABLE registry.portgroups (id INTEGER, "
+            "name TEXT, version TEXT COLLATE VERSION, size INTEGER, sha256 TEXT, "
+            "FOREIGN KEY(id) REFERENCES ports(id))",
+
         "COMMIT",
         NULL
     };
@@ -321,6 +326,36 @@
             continue;
         }
 
+        if (sql_version(NULL, -1, version, -1, "1.2") < 0) {
+            /* We need to add the portgroup table and move the portfiles out
+               of the db and into the filesystem. The latter is way easier to do
+               from Tcl, so here we'll just flag that it needs to be done. */
+            static char* version_1_2_queries[] = {
+                /* portgroups table */
+                "CREATE TABLE registry.portgroups (id INTEGER, "
+                    "name TEXT, version TEXT COLLATE VERSION, size INTEGER, sha256 TEXT, "
+                    "FOREIGN KEY(id) REFERENCES ports(id))",
+
+                "UPDATE registry.metadata SET value = '1.200' WHERE key = 'version'",
+
+                "INSERT INTO registry.metadata (key, value) VALUES ('portfiles_update_needed', 1)",
+
+                "COMMIT",
+                NULL
+            };
+
+            sqlite3_finalize(stmt);
+            stmt = NULL;
+
+            if (!do_queries(db, version_1_2_queries, errPtr)) {
+                rollback_db(db);
+                return 0;
+            }
+
+            did_update = 1;
+            continue;
+        }
+
         /* add new versions here, but remember to:
          *  - finalize the version query statement and set stmt to NULL
          *  - do _not_ use "BEGIN" in your query list, since a transaction has

Added: trunk/base/src/dedup_portfiles.tcl
===================================================================
--- trunk/base/src/dedup_portfiles.tcl	                        (rev 0)
+++ trunk/base/src/dedup_portfiles.tcl	2014-02-25 18:28:14 UTC (rev 117407)
@@ -0,0 +1,47 @@
+#!/usr/bin/env tclsh
+# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4
+# $Id$
+
+# move portfiles from sqlite db to filesystem, while deduplicating
+# Takes one argument, which should be TCL_PACKAGE_DIR.
+
+source [file join [lindex $argv 0] macports1.0 macports_fastload.tcl]
+package require macports 1.0
+package require registry2 2.0
+package require Pextlib 1.0
+
+umask 022
+
+array set ui_options {ports_verbose yes}
+
+mportinit ui_options
+
+if {[registry::metadata get portfiles_update_needed] == 1} {
+    set portfiles_dir [file join ${macports::registry.path} registry portfiles]
+
+    registry::write {
+        set installed_ports [registry::entry imaged]
+        foreach portref $installed_ports {
+            set portfile_contents [$portref portfile]
+            if {$portfile_contents ne "" && $portfile_contents ne "0"} {
+                set portfile_partial_dir [file join $portfiles_dir [$portref name]-[$portref version]_[$portref revision]]
+                file mkdir $portfile_partial_dir
+                set portfile_temp_path ${portfile_partial_dir}/Portfile
+                set fd [open $portfile_temp_path w]
+                puts $fd $portfile_contents
+                close $fd
+
+                set hash_size [sha256 file $portfile_temp_path]-[file size $portfile_temp_path]
+                set portfile_dir [file join $portfile_partial_dir $hash_size]
+                file mkdir $portfile_dir
+                file rename -force $portfile_temp_path $portfile_dir
+                file mtime ${portfile_dir}/Portfile [$portref date]
+
+                $portref portfile $hash_size
+            }
+        }
+        registry::metadata del portfiles_update_needed
+    }
+}
+
+exit 0


Property changes on: trunk/base/src/dedup_portfiles.tcl
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Modified: trunk/base/src/macports1.0/macports.tcl
===================================================================
--- trunk/base/src/macports1.0/macports.tcl	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/macports1.0/macports.tcl	2014-02-25 18:28:14 UTC (rev 117407)
@@ -1363,12 +1363,12 @@
     }
 
     foreach {opt val} $options {
-        $workername eval set user_options($opt) $val
-        $workername eval set $opt $val
+        $workername eval [list set user_options($opt) $val]
+        $workername eval [list set $opt $val]
     }
 
     foreach {var val} $variations {
-        $workername eval set variations($var) $val
+        $workername eval [list set variations($var) $val]
     }
 }
 
@@ -1715,12 +1715,7 @@
 proc mportopen_installed {name version revision variants options} {
     global macports::registry.path
     set regref [lindex [registry::entry imaged $name $version $revision $variants] 0]
-    set portfile_dir [file join ${registry.path} registry portfiles $name ${version}_${revision}$variants]
-    file mkdir $portfile_dir
-    set fd [open ${portfile_dir}/Portfile w]
-    puts $fd [$regref portfile]
-    close $fd
-    file mtime ${portfile_dir}/Portfile [$regref date]
+    set portfile_dir [file join ${registry.path} registry portfiles ${name}-${version}_${revision} [$regref portfile]]
 
     set variations {}
     set minusvariant [lrange [split [$regref negated_variants] -] 1 end]
@@ -1732,23 +1727,17 @@
         lappend variations $v -
     }
     lappend options subport $name
-    return [mportopen file://${portfile_dir}/ $options $variations]
-}
 
-# mportclose_installed
-# close mport opened with mportopen_installed and clean up associated files
-proc mportclose_installed {mport} {
-    global macports::registry.path
-    foreach key {subport version revision portvariants} {
-        set $key [_mportkey $mport $key]
+    # find portgroups in registry
+    set pgdirlist [list]
+    foreach pg [$regref groups_used] {
+        lappend pgdirlist [file join ${registry.path} registry portgroups [$pg sha256]-[$pg size]]
     }
-    mportclose $mport
-    set portfiles_dir [file join ${registry.path} registry portfiles $subport]
-    set portfile [file join $portfiles_dir ${version}_${revision}$portvariants Portfile]
-    file delete -force $portfile [file dirname $portfile]
-    if {[llength [glob -nocomplain -directory $portfiles_dir *]] == 0} {
-        file delete -force $portfiles_dir
+    if {$pgdirlist ne {}} {
+        lappend options _portgroup_search_dirs $pgdirlist
     }
+
+    return [mportopen file://${portfile_dir}/ $options $variations]
 }
 
 # Traverse a directory with ports, calling a function on the path of ports

Modified: trunk/base/src/macports1.0/tests/macports.test
===================================================================
--- trunk/base/src/macports1.0/tests/macports.test	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/macports1.0/tests/macports.test	2014-02-25 18:28:14 UTC (rev 117407)
@@ -153,10 +153,10 @@
        return "FAIL: installed port not opened"
     }
 
-    if {[catch {mportclose_installed $res}] != 0} {
+    if {[catch {mportclose $res}] != 0} {
        return "FAIL: cannot close port"
     }
-    if {[catch {mportclose_installed $res}] != 1} {
+    if {[catch {mportclose $res}] != 1} {
        return "FAIL: installed port not closed"
     }
     return "Installed port open successful."
@@ -702,10 +702,6 @@
 # test mportopen
 
 
-# Covered by mportopen_installed
-# test mportclose_installed
-
-
 test mporttraverse {
     Mport traverse unit test. Uses 3rd column of the Portfile.
 } -setup {

Modified: trunk/base/src/pextlib1.0/tracelib.c
===================================================================
--- trunk/base/src/pextlib1.0/tracelib.c	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/pextlib1.0/tracelib.c	2014-02-25 18:28:14 UTC (rev 117407)
@@ -56,6 +56,7 @@
 #include <sys/un.h>
 #include <unistd.h>
 
+#include <cregistry/portgroup.h>
 #include <cregistry/entry.h>
 #include <registry2.0/registry.h>
 

Modified: trunk/base/src/port1.0/portinstall.tcl
===================================================================
--- trunk/base/src/port1.0/portinstall.tcl	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/port1.0/portinstall.tcl	2014-02-25 18:28:14 UTC (rev 117407)
@@ -307,7 +307,7 @@
     homepage depends_run package-install workdir workpath \
     worksrcdir UI_PREFIX destroot revision maintainers user_options \
     portvariants negated_variants targets depends_lib PortInfo epoch license \
-    os.platform os.major portarchivetype installPlist
+    os.platform os.major portarchivetype installPlist registry.path porturl
 
     set oldpwd [pwd]
     if {$oldpwd eq ""} {
@@ -373,11 +373,40 @@
             # register files
             $regref map $installPlist
         }
-        
+
         # store portfile
-        set fd [open [file join ${portpath} Portfile]]
-        $regref portfile [read $fd]
-        close $fd
+        set portfile_path [file join $portpath Portfile]
+        set portfile_sha256 [sha256 file $portfile_path]
+        set portfile_size [file size $portfile_path]
+        set portfile_reg_dir [file join ${registry.path} registry portfiles ${subport}-${version}_${revision} ${portfile_sha256}-${portfile_size}]
+        file mkdir $portfile_reg_dir
+        set portfile_reg_path ${portfile_reg_dir}/Portfile
+        if {![file isfile $portfile_reg_path] || [file size $portfile_reg_path] != $portfile_size || [sha256 file $portfile_reg_path] ne $portfile_sha256} {
+            file copy -force $portfile_path $portfile_reg_dir
+        }
+        $regref portfile ${portfile_sha256}-${portfile_size}
+
+        # store portgroups
+        if {[info exists PortInfo(portgroups)]} {
+            foreach pg $PortInfo(portgroups) {
+                set pgname [lindex $pg 0]
+                set pgversion [lindex $pg 1]
+                set groupFile [getportresourcepath $porturl "port1.0/group/${pgname}-${pgversion}.tcl"]
+                if {[file isfile $groupFile]} {
+                    set pgsha256 [sha256 file $groupFile]
+                    set pgsize [file size $groupFile]
+                    set pg_reg_dir [file join ${registry.path} registry portgroups ${pgsha256}-${pgsize}]
+                    set pg_reg_path ${pg_reg_dir}/${pgname}-${pgversion}.tcl
+                    if {![file isfile $pg_reg_path] || [file size $pg_reg_path] != $pgsize || [sha256 file $pg_reg_path] ne $pgsha256} {
+                        file mkdir $pg_reg_dir
+                        file copy -force $groupFile $pg_reg_dir
+                    }
+                    $regref addgroup $pgname $pgversion $pgsha256 $pgsize
+                } else {
+                    ui_debug "install_main: no portgroup ${pgname}-${pgversion}.tcl found"
+                }
+            }
+        }
     }
 
     _cd $oldpwd

Modified: trunk/base/src/port1.0/portutil.tcl
===================================================================
--- trunk/base/src/port1.0/portutil.tcl	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/port1.0/portutil.tcl	2014-02-25 18:28:14 UTC (rev 117407)
@@ -2569,12 +2569,12 @@
 
 # Use a specified group/version.
 proc PortGroup {group version} {
-    global porturl PortInfo
+    global porturl PortInfo _portgroup_search_dirs
 
     lappend PortInfo(portgroups) [list $group $version]
 
-    if {[info exists portutil::portgroup_search_dirs]} {
-        foreach dir $portutil::portgroup_search_dirs {
+    if {[info exists _portgroup_search_dirs]} {
+        foreach dir $_portgroup_search_dirs {
             set groupFile ${dir}/${group}-${version}.tcl
             if {[file exists $groupFile]} {
                 uplevel "source $groupFile"

Modified: trunk/base/src/registry2.0/Makefile
===================================================================
--- trunk/base/src/registry2.0/Makefile	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/Makefile	2014-02-25 18:28:14 UTC (rev 117407)
@@ -3,7 +3,8 @@
 SRCS = registry.tcl registry_autoconf.tcl registry_util.tcl receipt_flat.tcl receipt_sqlite.tcl portimage.tcl portuninstall.tcl
 OBJS = registry.o util.o \
 	entry.o entryobj.o \
-	file.o fileobj.o
+	file.o fileobj.o \
+	portgroup.o portgroupobj.o
 	#graph.o graphobj.o
 
 SHLIB_NAME= registry${SHLIB_SUFFIX}

Modified: trunk/base/src/registry2.0/entryobj.c
===================================================================
--- trunk/base/src/registry2.0/entryobj.c	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/entryobj.c	2014-02-25 18:28:14 UTC (rev 117407)
@@ -356,6 +356,59 @@
     }
 }
 
+static int entry_obj_add_portgroup(Tcl_Interp* interp, reg_entry* entry, int objc,
+        Tcl_Obj* CONST objv[]) {
+    reg_registry* reg = registry_for(interp, reg_attached);
+    if (objc != 6) {
+        Tcl_WrongNumArgs(interp, 1, objv, "addgroup name version sha256 size");
+        return TCL_ERROR;
+    } else if (reg == NULL) {
+        return TCL_ERROR;
+    } else {
+        reg_error error;
+        char* name = Tcl_GetString(objv[2]);
+        char* version = Tcl_GetString(objv[3]);
+        char* sha256 = Tcl_GetString(objv[4]);
+        Tcl_WideInt tclsize;
+        Tcl_GetWideIntFromObj(interp, objv[5], &tclsize);
+        sqlite_int64 size = (sqlite_int64)tclsize;
+        if (reg_entry_addgroup(entry, name, version, sha256, size, &error)) {
+            return TCL_OK;
+        }
+        return registry_failed(interp, &error);
+    }
+}
+
+static int entry_obj_get_portgroups(Tcl_Interp* interp, reg_entry* entry, int objc,
+        Tcl_Obj* CONST objv[]) {
+    reg_registry* reg = registry_for(interp, reg_attached);
+    if (objc != 2) {
+        Tcl_WrongNumArgs(interp, 1, objv, "groups_used");
+        return TCL_ERROR;
+    } else if (reg == NULL) {
+        return TCL_ERROR;
+    } else {
+        reg_portgroup** portgroups;
+        reg_error error;
+        int portgroup_count = reg_entry_getgroups(entry, &portgroups, &error);
+        if (portgroup_count >= 0) {
+            Tcl_Obj** objs;
+            int retval = TCL_ERROR;
+            if (list_portgroup_to_obj(interp, &objs, portgroups, portgroup_count, &error)){
+                Tcl_Obj* result = Tcl_NewListObj(portgroup_count, objs);
+                Tcl_SetObjResult(interp, result);
+                free(objs);
+                retval = TCL_OK;
+            } else {
+                retval = registry_failed(interp, &error);
+            }
+            free(portgroups);
+            return retval;
+        }
+        return registry_failed(interp, &error);
+    }
+}
+
 typedef struct {
     char* name;
     int (*function)(Tcl_Interp* interp, reg_entry* entry, int objc,
@@ -391,6 +444,9 @@
     { "dependents", entry_obj_dependents },
     { "dependencies", entry_obj_dependencies },
     { "depends", entry_obj_depends },
+    /* portgroups */
+    { "addgroup", entry_obj_add_portgroup },
+    { "groups_used", entry_obj_get_portgroups },
     { NULL, NULL }
 };
 

Added: trunk/base/src/registry2.0/portgroup.c
===================================================================
--- trunk/base/src/registry2.0/portgroup.c	                        (rev 0)
+++ trunk/base/src/registry2.0/portgroup.c	2014-02-25 18:28:14 UTC (rev 117407)
@@ -0,0 +1,294 @@
+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sqlite3.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+
+#include <cregistry/portgroup.h>
+#include <cregistry/util.h>
+
+#include "portgroup.h"
+#include "portgroupobj.h"
+#include "registry.h"
+#include "util.h"
+
+/**
+ * Converts a command name into a `reg_portgroup`.
+ *
+ * @param [in] interp  Tcl interpreter to check within
+ * @param [in] name    name of portgroup to get
+ * @param [out] errPtr description of error if the portgroup can't be found
+ * @return             a portgroup, or NULL if one couldn't be found
+ * @see get_object
+ */
+static reg_portgroup* get_portgroup(Tcl_Interp* interp, char* name, reg_error* errPtr) {
+    return (reg_portgroup*)get_object(interp, name, "portgroup", portgroup_obj_cmd, errPtr);
+}
+
+/**
+ * Removes the portgroup from the Tcl interpreter. Doesn't actually delete it since
+ * that's the registry's job. This is written to be used as the
+ * `Tcl_CmdDeleteProc` for an portgroup object command.
+ *
+ * @param [in] clientData address of a reg_portgroup to remove
+ */
+void delete_portgroup(ClientData clientData) {
+    reg_portgroup* portgroup = (reg_portgroup*)clientData;
+    free(portgroup->proc);
+    portgroup->proc = NULL;
+}
+
+/**
+ * registry::portgroup open id name version size sha256
+ *
+ * Opens a portgroup matching the given parameters.
+ */
+static int portgroup_open(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
+	reg_registry* reg = registry_for(interp, reg_attached);
+	if (objc != 7) {
+		Tcl_WrongNumArgs(interp, 1, objv, "open id name version size sha256");
+		return TCL_ERROR;
+	} else if (reg == NULL) {
+		return TCL_ERROR;
+	} else {
+		char* id = Tcl_GetString(objv[2]);
+		char* name = Tcl_GetString(objv[3]);
+        char* version = Tcl_GetString(objv[4]);
+        char* size = Tcl_GetString(objv[5]);
+        char* sha256 = Tcl_GetString(objv[6]);
+
+		reg_error error;
+		reg_portgroup* portgroup = reg_portgroup_open(reg, id, name, version, size, sha256, &error);
+		if (portgroup != NULL) {
+			Tcl_Obj* result;
+			if (portgroup_to_obj(interp, &result, portgroup, NULL, &error)) {
+				Tcl_SetObjResult(interp, result);
+				return TCL_OK;
+			}
+		}
+		return registry_failed(interp, &error);
+	}
+	return TCL_ERROR;
+}
+
+/**
+ * registry::portgroup close portgroup
+ *
+ * Closes a portgroup. It will remain in the registry.
+ */
+static int portgroup_close(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
+	if (objc != 3) {
+		Tcl_WrongNumArgs(interp, 1, objv, "close portgroup");
+		return TCL_ERROR;
+	} else {
+		reg_error error;
+		char* proc = Tcl_GetString(objv[2]);
+		reg_portgroup* portgroup = get_portgroup(interp, proc, &error);
+		if (portgroup == NULL) {
+			return registry_failed(interp, &error);
+		} else {
+			Tcl_DeleteCommand(interp, proc);
+			return TCL_OK;
+		}
+	}
+}
+
+typedef struct {
+    char* name;
+    reg_strategy strategy;
+} strategy_type;
+
+static strategy_type strategies[] = {
+    { "-exact",  reg_strategy_exact },
+    { "-glob",   reg_strategy_glob },
+    { "-regexp", reg_strategy_regexp },
+    { "-null",   reg_strategy_null },
+    { "--",      reg_strategy_exact },
+    { NULL, 0 }
+};
+
+/*
+ * registry::portgroup search ?key value ...?
+ *
+ * Searches the registry for portgroups for which each key's value is equal to the
+ * given value. To find all portgroups, call `portgroup search` with no key-value pairs.
+ * For each key, can be given an option of -exact, -glob, -regexp or -null to
+ * specify the matching strategy; defaults to exact.
+ */
+static int portgroup_search(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]) {
+    int i, j;
+    reg_registry* reg = registry_for(interp, reg_attached);
+    if (reg == NULL) {
+        return TCL_ERROR;
+    } else {
+        char** keys;
+        char** vals;
+        int* strats;
+        int key_count = 0;
+        reg_portgroup** portgroups;
+        reg_error error;
+        int portgroup_count;
+        for (i = 2; i < objc;) {
+            int index, strat_index, val_length;
+            if (Tcl_GetIndexFromObj(interp, objv[i], portgroup_props, "search key",
+                        0, &index) != TCL_OK) {
+                return TCL_ERROR;
+            }
+
+            /* we ate the key value */
+            i++;
+
+            /* check whether there's a strategy */
+            if (Tcl_GetString(objv[i])[0] == '-'
+                    && Tcl_GetIndexFromObjStruct(interp, objv[i], strategies,
+                        sizeof(strategy_type), "option", 0, &strat_index)
+                    != TCL_ERROR) {
+                /* this key has a strategy specified, eat the strategy parameter */
+                i++;
+
+                if (strategies[strat_index].strategy != reg_strategy_null) {
+                    /* this key must also have a value */
+
+                    if (Tcl_GetStringFromObj(objv[i], &val_length) == NULL
+                            || val_length == 0) {
+                        Tcl_WrongNumArgs(interp, 2, objv,
+                                "search ?key ?options? value ...?");
+                        return TCL_ERROR;
+                    }
+
+                    i++;
+                }
+            } else {
+                /* this key must also have a value */
+
+                if (Tcl_GetStringFromObj(objv[i], &val_length) == NULL
+                        || val_length == 0) {
+                    Tcl_WrongNumArgs(interp, 2, objv,
+                            "search ?key ?options? value ...?");
+                    return TCL_ERROR;
+                }
+
+                i++;
+            }
+
+            key_count++;
+        }
+
+        keys = malloc(key_count * sizeof(char*));
+        vals = malloc(key_count * sizeof(char*));
+        strats = malloc(key_count * sizeof(int));
+        if (!keys || !vals || !strats) {
+            return TCL_ERROR;
+        }
+        for (i = 2, j = 0; i < objc && j < key_count; j++) {
+            int strat_index;
+
+            keys[j] = Tcl_GetString(objv[i++]);
+
+            /* try to get the strategy */
+            if (Tcl_GetString(objv[i])[0] == '-'
+                    && Tcl_GetIndexFromObjStruct(interp, objv[i], strategies,
+                        sizeof(strategy_type), "option", 0, &strat_index)
+                    != TCL_ERROR) {
+                /* this key has a strategy specified */
+                i++;
+
+                strats[j] = strategies[strat_index].strategy;
+            } else {
+                /* use default strategy */
+                strats[j] = reg_strategy_exact;
+            }
+
+            if (strats[j] != reg_strategy_null) {
+                vals[j] = Tcl_GetString(objv[i++]);
+            } else {
+                vals[j] = NULL;
+            }
+        }
+        portgroup_count = reg_portgroup_search(reg, keys, vals, strats, key_count,
+                &portgroups, &error);
+        free(keys);
+        free(vals);
+        free(strats);
+        if (portgroup_count >= 0) {
+            int retval;
+            Tcl_Obj* resultObj;
+            Tcl_Obj** objs;
+            if (list_portgroup_to_obj(interp, &objs, portgroups, portgroup_count, &error)){
+                resultObj = Tcl_NewListObj(portgroup_count, objs);
+                Tcl_SetObjResult(interp, resultObj);
+                free(objs);
+                retval = TCL_OK;
+            } else {
+                retval = registry_failed(interp, &error);
+            }
+            free(portgroups);
+            return retval;
+        }
+        return registry_failed(interp, &error);
+    }
+}
+
+typedef struct {
+    char* name;
+    int (*function)(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
+} portgroup_cmd_type;
+
+static portgroup_cmd_type portgroup_cmds[] = {
+    /* Global commands */
+    { "open", portgroup_open },
+    { "close", portgroup_close },
+    { "search", portgroup_search },
+    { NULL, NULL }
+};
+
+/*
+ * registry::portgroup cmd ?arg ...?
+ *
+ * Commands manipulating portgroup entries in the registry. This can be called `registry::portgroup`
+ */
+int portgroup_cmd(ClientData clientData UNUSED, Tcl_Interp* interp, int objc,
+        Tcl_Obj* CONST objv[]) {
+    int cmd_index;
+    if (objc < 2) {
+        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?");
+        return TCL_ERROR;
+    }
+    if (Tcl_GetIndexFromObjStruct(interp, objv[1], portgroup_cmds,
+                sizeof(portgroup_cmd_type), "cmd", 0, &cmd_index) == TCL_OK) {
+        portgroup_cmd_type* cmd = &portgroup_cmds[cmd_index];
+        return cmd->function(interp, objc, objv);
+    }
+    return TCL_ERROR;
+}

Added: trunk/base/src/registry2.0/portgroup.h
===================================================================
--- trunk/base/src/registry2.0/portgroup.h	                        (rev 0)
+++ trunk/base/src/registry2.0/portgroup.h	2014-02-25 18:28:14 UTC (rev 117407)
@@ -0,0 +1,42 @@
+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ */
+#ifndef _PORTGROUP_H
+#define _PORTGROUP_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <tcl.h>
+
+void delete_portgroup(ClientData clientData);
+
+int portgroup_cmd(ClientData clientData UNUSED, Tcl_Interp* interp, int objc,
+        Tcl_Obj* CONST objv[]);
+
+#endif /* _PORTGROUP_H */

Added: trunk/base/src/registry2.0/portgroupobj.c
===================================================================
--- trunk/base/src/registry2.0/portgroupobj.c	                        (rev 0)
+++ trunk/base/src/registry2.0/portgroupobj.c	2014-02-25 18:28:14 UTC (rev 117407)
@@ -0,0 +1,135 @@
+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <tcl.h>
+#include <sqlite3.h>
+
+#include "portgroupobj.h"
+#include "registry.h"
+#include "util.h"
+
+const char* portgroup_props[] = {
+    "name",
+    "version",
+    "size",
+    "sha256",
+    NULL
+};
+
+/* ${portgroup} prop ?value? */
+static int portgroup_obj_prop(Tcl_Interp* interp, reg_portgroup* portgroup, int objc,
+        Tcl_Obj* CONST objv[]) {
+    int index;
+    if (objc > 3) {
+        Tcl_WrongNumArgs(interp, 2, objv, "?value?");
+        return TCL_ERROR;
+    }
+    if (objc == 2) {
+        /* ${portgroup} prop; return the current value */
+        reg_registry* reg = registry_for(interp, reg_attached);
+        if (reg == NULL) {
+            return TCL_ERROR;
+        }
+        if (Tcl_GetIndexFromObj(interp, objv[1], portgroup_props, "prop", 0, &index)
+                == TCL_OK) {
+            char* key = Tcl_GetString(objv[1]);
+            char* value;
+            reg_error error;
+            if (reg_portgroup_propget(portgroup, key, &value, &error)) {
+                Tcl_Obj* result = Tcl_NewStringObj(value, -1);
+                Tcl_SetObjResult(interp, result);
+                free(value);
+                return TCL_OK;
+            }
+            return registry_failed(interp, &error);
+        }
+        return TCL_ERROR;
+    } else {
+        /* ${portgroup} prop name value; set a new value */
+        reg_registry* reg = registry_for(interp, reg_attached);
+        if (reg == NULL) {
+            return TCL_ERROR;
+        }
+        if (Tcl_GetIndexFromObj(interp, objv[1], portgroup_props, "prop", 0, &index)
+                == TCL_OK) {
+            char* key = Tcl_GetString(objv[1]);
+            char* value = Tcl_GetString(objv[2]);
+            reg_error error;
+            if (reg_portgroup_propset(portgroup, key, value, &error)) {
+                return TCL_OK;
+            }
+            return registry_failed(interp, &error);
+        }
+        return TCL_ERROR;
+    }
+}
+
+typedef struct {
+    char* name;
+    int (*function)(Tcl_Interp* interp, reg_portgroup* portgroup, int objc,
+            Tcl_Obj* CONST objv[]);
+} portgroup_obj_cmd_type;
+
+static portgroup_obj_cmd_type portgroup_cmds[] = {
+    /* keys */
+    { "name", portgroup_obj_prop },
+    { "version", portgroup_obj_prop },
+    { "size", portgroup_obj_prop },
+    { "sha256", portgroup_obj_prop },
+    { NULL, NULL }
+};
+
+/* ${portgroup} cmd ?arg ...? */
+/* This function implements the command that will be called when a portgroup
+ * created by `registry::portgroup` is used as a procedure. Since all data is kept
+ * in a temporary sqlite3 database that is created for the current interpreter,
+ * none of the sqlite3 functions used have any error checking. That should be a
+ * safe assumption, since nothing outside of registry:: should ever have the
+ * chance to touch it.
+ */
+int portgroup_obj_cmd(ClientData clientData, Tcl_Interp* interp, int objc,
+        Tcl_Obj* CONST objv[]) {
+    int cmd_index;
+    if (objc < 2) {
+        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?");
+        return TCL_ERROR;
+    }
+    if (Tcl_GetIndexFromObjStruct(interp, objv[1], portgroup_cmds,
+                sizeof(portgroup_obj_cmd_type), "cmd", 0, &cmd_index) == TCL_OK) {
+        portgroup_obj_cmd_type* cmd = &portgroup_cmds[cmd_index];
+        return cmd->function(interp, (reg_portgroup*)clientData, objc, objv);
+    }
+    return TCL_ERROR;
+}
+

Added: trunk/base/src/registry2.0/portgroupobj.h
===================================================================
--- trunk/base/src/registry2.0/portgroupobj.h	                        (rev 0)
+++ trunk/base/src/registry2.0/portgroupobj.h	2014-02-25 18:28:14 UTC (rev 117407)
@@ -0,0 +1,43 @@
+/*
+ * vim:tw=80:expandtab
+ * $Id$
+ *
+ * Copyright (c) 2014 The MacPorts Project
+ * All rights reserved.
+ *
+ * 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.
+ */
+#ifndef _PORTGROUP_OBJ_CMD_H
+#define _PORTGROUP_OBJ_CMD_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <tcl.h>
+#include <sqlite3.h>
+
+extern const char* portgroup_props[];
+
+int portgroup_obj_cmd(ClientData clientData, Tcl_Interp* interp, int objc,
+        Tcl_Obj* CONST objv[]);
+
+#endif /* _PORTGROUP_OBJ_CMD_H */

Modified: trunk/base/src/registry2.0/portuninstall.tcl
===================================================================
--- trunk/base/src/registry2.0/portuninstall.tcl	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/portuninstall.tcl	2014-02-25 18:28:14 UTC (rev 117407)
@@ -53,7 +53,7 @@
 
 proc uninstall {portname {version ""} {revision ""} {variants 0} {optionslist ""}} {
     global uninstall.force uninstall.nochecksum UI_PREFIX \
-           macports::portimagefilepath
+           macports::portimagefilepath macports::registry.path
     array set options $optionslist
     if {[info exists options(subport)]} {
         # don't want this set when calling registry::run_target
@@ -156,7 +156,7 @@
         # look up deps from the saved portfile if possible
         if {![catch {set mport [mportopen_installed [$port name] [$port version] [$port revision] [$port variants] $optionslist]}]} {
             array set depportinfo [mportinfo $mport]
-            mportclose_installed $mport
+            mportclose $mport
             foreach type $deptypes {
                 if {[info exists depportinfo($type)]} {
                     foreach dep $depportinfo($type) {
@@ -217,9 +217,37 @@
         # files so just ignore the failure
         catch {file delete [::file dirname $imagefile]}
 
+        # We want to delete the portfile if not referenced by any other ports
+        set portfile [$ref portfile]
+
+        # and likewise the portgroups
+        set portgroups [list]
+        foreach pg [$ref groups_used] {
+            lappend portgroups [list [$pg name] [$pg version] [$pg size] [$pg sha256]]
+        }
+
         registry::write {
             registry::entry delete $port
         }
+
+        set portfile_path [file join ${registry.path} registry portfiles ${portname}-${version}_${revision} $portfile]
+        if {[registry::entry search portfile $portfile] eq {}} {
+            file delete -force $portfile_path
+            catch {file delete [file dirname $portfile_path]}
+        }
+
+        set reg_portgroups_dir [file join ${registry.path} registry portgroups]
+        foreach pg $portgroups {
+            set pgname [lindex $pg 0]
+            set pgversion [lindex $pg 1]
+            set pgsize [lindex $pg 2]
+            set pgsha256 [lindex $pg 3]
+            if {[registry::portgroup search name $pgname version $pgversion size $pgsize sha256 $pgsha256] eq {}} {
+                set pg_reg_dir [file join $reg_portgroups_dir ${pgsha256}-${pgsize}]
+                file delete -force ${pg_reg_dir}/${pgname}-${pgversion}.tcl
+                catch {file delete $pg_reg_dir}
+            }
+        }
     }
     
     # uninstall dependencies if requested

Modified: trunk/base/src/registry2.0/registry.c
===================================================================
--- trunk/base/src/registry2.0/registry.c	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/registry.c	2014-02-25 18:28:14 UTC (rev 117407)
@@ -3,7 +3,7 @@
  * $Id$
  *
  * Copyright (c) 2007 Chris Pickel <sfiera at macports.org>
- * Copyright (c) 2012 The MacPorts Project
+ * Copyright (c) 2012, 2014 The MacPorts Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -38,6 +38,7 @@
 #include <tcl.h>
 
 #include <cregistry/registry.h>
+#include <cregistry/portgroup.h>
 #include <cregistry/entry.h>
 #include <cregistry/file.h>
 
@@ -46,6 +47,7 @@
 #include "file.h"
 #include "graph.h"
 #include "item.h"
+#include "portgroup.h"
 #include "registry.h"
 #include "util.h"
 
@@ -389,6 +391,7 @@
     /* Tcl_CreateObjCommand(interp, "registry::item", item_cmd, NULL, NULL); */
     Tcl_CreateObjCommand(interp, "registry::entry", entry_cmd, NULL, NULL);
     Tcl_CreateObjCommand(interp, "registry::file", file_cmd, NULL, NULL);
+    Tcl_CreateObjCommand(interp, "registry::portgroup", portgroup_cmd, NULL, NULL);
     Tcl_CreateObjCommand(interp, "registry::metadata", metadata_cmd, NULL, NULL);
     if (Tcl_PkgProvide(interp, "registry2", "2.0") != TCL_OK) {
         return TCL_ERROR;

Modified: trunk/base/src/registry2.0/registry.h
===================================================================
--- trunk/base/src/registry2.0/registry.h	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/registry.h	2014-02-25 18:28:14 UTC (rev 117407)
@@ -34,6 +34,7 @@
 
 #include <tcl.h>
 #include <sqlite3.h>
+#include <cregistry/portgroup.h>
 #include <cregistry/entry.h>
 
 typedef struct _entry_list {

Modified: trunk/base/src/registry2.0/registry_util.tcl
===================================================================
--- trunk/base/src/registry2.0/registry_util.tcl	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/registry_util.tcl	2014-02-25 18:28:14 UTC (rev 117407)
@@ -103,7 +103,7 @@
         if {[catch {set result [mportexec $mport $target]} result] || $result != 0} {
             global errorInfo
             ui_debug "$errorInfo"
-            catch {mportclose_installed $mport}
+            catch {mportclose $mport}
             ui_warn "Failed to execute portfile from registry for $portspec"
             switch $target {
                 activate {
@@ -127,7 +127,7 @@
             if {(![info exists keeplogs] || !$keeplogs) && $target ne "activate"} {
                 catch {mportexec $mport clean}
             }
-            mportclose_installed $mport
+            mportclose $mport
             return 1
         }
     } else {

Modified: trunk/base/src/registry2.0/util.c
===================================================================
--- trunk/base/src/registry2.0/util.c	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/util.c	2014-02-25 18:28:14 UTC (rev 117407)
@@ -38,6 +38,7 @@
 #include "util.h"
 #include "entryobj.h"
 #include "fileobj.h"
+#include "portgroupobj.h"
 
 /**
  * Generates a unique proc name starting with prefix.
@@ -214,6 +215,29 @@
 }
 
 /**
+ * Sets a given name to be a portgroup object.
+ *
+ * @param [in] interp  Tcl interpreter to create the portgroup within
+ * @param [in] name    name to associate the given portgroup with
+ * @param [in] portgroup    portgroup to associate with the given name
+ * @param [out] errPtr description of error if it couldn't be set
+ * @return             true if success; false if failure
+ * @see set_object
+ */
+int set_portgroup(Tcl_Interp* interp, char* name, reg_portgroup* portgroup,
+        reg_error* errPtr) {
+    if (set_object(interp, name, portgroup, "portgroup", portgroup_obj_cmd, NULL,
+                errPtr)) {
+        portgroup->proc = strdup(name);
+        if (!portgroup->proc) {
+            return 0;
+        }
+        return 1;
+    }
+    return 0;
+}
+
+/**
  * Reports a sqlite3 error to Tcl.
  *
  * Queries the database for the most recent error message and sets it as the
@@ -297,6 +321,23 @@
     return 1;
 }
 
+int portgroup_to_obj(Tcl_Interp* interp, Tcl_Obj** obj, reg_portgroup* portgroup,
+        int* lower_bound, reg_error* errPtr) {
+    if (portgroup->proc == NULL) {
+        char* name = unique_name(interp, "::registry::portgroup", lower_bound);
+        if (!name) {
+            return 0;
+        }
+        if (!set_portgroup(interp, name, portgroup, errPtr)) {
+            free(name);
+            return 0;
+        }
+        free(name);
+    }
+    *obj = Tcl_NewStringObj(portgroup->proc, -1);
+    return 1;
+}
+
 int list_entry_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
         reg_entry** entries, int entry_count, reg_error* errPtr) {
     int lower_bound = 0;
@@ -311,6 +352,13 @@
             (void***)objs, (void**)files, file_count, errPtr);
 }
 
+int list_portgroup_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
+        reg_portgroup** portgroups, int portgroup_count, reg_error* errPtr) {
+    int lower_bound = 0;
+    return recast(interp, (cast_function*)portgroup_to_obj, &lower_bound, NULL,
+            (void***)objs, (void**)portgroups, portgroup_count, errPtr);
+}
+
 static int obj_to_string(void* userdata UNUSED, char** string, Tcl_Obj* obj,
         void* param UNUSED, reg_error* errPtr UNUSED) {
     *string = Tcl_GetString(obj);

Modified: trunk/base/src/registry2.0/util.h
===================================================================
--- trunk/base/src/registry2.0/util.h	2014-02-25 16:10:21 UTC (rev 117406)
+++ trunk/base/src/registry2.0/util.h	2014-02-25 18:28:14 UTC (rev 117407)
@@ -37,6 +37,7 @@
 #include <sqlite3.h>
 
 #include <cregistry/registry.h>
+#include <cregistry/portgroup.h>
 #include <cregistry/entry.h>
 #include <cregistry/file.h>
 
@@ -60,6 +61,8 @@
         reg_error* errPtr);
 int set_file(Tcl_Interp* interp, char* name, reg_file* file,
         reg_error* errPtr);
+int set_portgroup(Tcl_Interp* interp, char* name, reg_portgroup* portgroup,
+        reg_error* errPtr);
 
 void set_sqlite_result(Tcl_Interp* interp, sqlite3* db, const char* query);
 
@@ -77,6 +80,10 @@
         int* lower_bound, reg_error* errPtr);
 int list_file_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
         reg_file** files, int file_count, reg_error* errPtr);
+int portgroup_to_obj(Tcl_Interp* interp, Tcl_Obj** ibj, reg_portgroup* portgroup,
+        int* lower_bound, reg_error* errPtr);
+int list_portgroup_to_obj(Tcl_Interp* interp, Tcl_Obj*** objs,
+        reg_portgroup** portgroups, int portgroup_count, reg_error* errPtr);
 
 void free_strings(void* userdata UNUSED, char** strings, int count);
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/macports-changes/attachments/20140225/887f6c26/attachment-0001.html>


More information about the macports-changes mailing list