Revision: 117407 https://trac.macports.org/changeset/117407 Author: jmr@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@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(®->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@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(®->open_files, TCL_STRING_KEYS); + Tcl_InitHashTable(®->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@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@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@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);
participants (1)
-
jmr@macports.org