[darwinbuild-changes] [773] trunk

source_changes at macosforge.org source_changes at macosforge.org
Fri Mar 12 14:20:44 PST 2010


Revision: 773
          http://trac.macosforge.org/projects/darwinbuild/changeset/773
Author:   wsiegrist at apple.com
Date:     2010-03-12 14:20:40 -0800 (Fri, 12 Mar 2010)
Log Message:
-----------
Merge PR-7593824

Modified Paths:
--------------
    trunk/darwinup/Archive.cpp
    trunk/darwinup/Archive.h
    trunk/darwinup/Column.cpp
    trunk/darwinup/Column.h
    trunk/darwinup/DB.cpp
    trunk/darwinup/DB.h
    trunk/darwinup/Database.cpp
    trunk/darwinup/Database.h
    trunk/darwinup/Depot.cpp
    trunk/darwinup/Depot.h
    trunk/darwinup/Table.cpp
    trunk/darwinup/Table.h
    trunk/darwinup/Utils.cpp
    trunk/darwinup/Utils.h
    trunk/darwinup/main.cpp
    trunk/testing/darwinup/run-tests.sh

Property Changed:
----------------
    trunk/
    trunk/darwinbuild/darwinbuild.in
    trunk/darwinbuild/darwinmaster.in
    trunk/darwinbuild/installXcode.in
    trunk/darwinbuild/packageRoots.in
    trunk/darwinbuild/thinPackages.in


Property changes on: trunk
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388:399-419
/branches/PR-6358021:442-443
/branches/PR-6392966:423-427
/branches/PR-6398060:433-434
/branches/PR-6493844:460-461
/branches/PR-6497694:466-468,471
/branches/PR-6634286:632-650
/branches/PR-6688645:479-490
/branches/PR-6722857:495-499
/branches/PR-6729491:655-664
/branches/PR-7250612:635-650
/branches/PR-7341154:682-694
/branches/PR-7431723:660-664
/branches/PR-7461534:650-664
/branches/PR-7482850:670-671
/branches/PR-7489777:676-731
/branches/PR-7529688:692-694
/branches/PR-7598640:703-731
/trunk:432-434
   + /branches/PR-4841388:399-419
/branches/PR-6358021:442-443
/branches/PR-6392966:423-427
/branches/PR-6398060:433-434
/branches/PR-6493844:460-461
/branches/PR-6497694:466-468,471
/branches/PR-6634286:632-650
/branches/PR-6688645:479-490
/branches/PR-6722857:495-499
/branches/PR-6729491:655-664
/branches/PR-7250612:635-650
/branches/PR-7341154:682-694
/branches/PR-7431723:660-664
/branches/PR-7461534:650-664
/branches/PR-7482850:670-671
/branches/PR-7489777:676-731
/branches/PR-7529688:692-694
/branches/PR-7593824:739-772
/branches/PR-7598640:703-731


Property changes on: trunk/darwinbuild/darwinbuild.in
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388/darwinbuild/darwinbuild:399-419
/branches/PR-6358021/darwinbuild/darwinbuild:442-443
/branches/PR-6392966/darwinbuild/darwinbuild:423-427
/branches/PR-6398060/darwinbuild/darwinbuild:433-434
/branches/PR-6493844/darwinbuild/darwinbuild.in:460-461
/branches/PR-6497694/darwinbuild/darwinbuild.in:466-468,471
/branches/PR-6634286/darwinbuild/darwinbuild.in:632-650
/branches/PR-6688645/darwinbuild/darwinbuild.in:479-490
/branches/PR-6722857/darwinbuild/darwinbuild.in:495-499
/branches/PR-6729491/darwinbuild/darwinbuild.in:655-664
/branches/PR-7250612/darwinbuild/darwinbuild.in:635-650
/branches/PR-7341154/darwinbuild/darwinbuild.in:682-694
/branches/PR-7431723/darwinbuild/darwinbuild.in:660-664
/branches/PR-7461534/darwinbuild/darwinbuild.in:650-664
/branches/PR-7489777/darwinbuild/darwinbuild.in:676-731
/branches/PR-7529688/darwinbuild/darwinbuild.in:692-694
/branches/PR-7598640/darwinbuild/darwinbuild.in:703-731
/trunk/darwinbuild/darwinbuild:432-434
   + /branches/PR-4841388/darwinbuild/darwinbuild:399-419
/branches/PR-6358021/darwinbuild/darwinbuild:442-443
/branches/PR-6392966/darwinbuild/darwinbuild:423-427
/branches/PR-6398060/darwinbuild/darwinbuild:433-434
/branches/PR-6493844/darwinbuild/darwinbuild.in:460-461
/branches/PR-6497694/darwinbuild/darwinbuild.in:466-468,471
/branches/PR-6634286/darwinbuild/darwinbuild.in:632-650
/branches/PR-6688645/darwinbuild/darwinbuild.in:479-490
/branches/PR-6722857/darwinbuild/darwinbuild.in:495-499
/branches/PR-6729491/darwinbuild/darwinbuild.in:655-664
/branches/PR-7250612/darwinbuild/darwinbuild.in:635-650
/branches/PR-7341154/darwinbuild/darwinbuild.in:682-694
/branches/PR-7431723/darwinbuild/darwinbuild.in:660-664
/branches/PR-7461534/darwinbuild/darwinbuild.in:650-664
/branches/PR-7489777/darwinbuild/darwinbuild.in:676-731
/branches/PR-7529688/darwinbuild/darwinbuild.in:692-694
/branches/PR-7593824/darwinbuild/darwinbuild.in:739-772
/branches/PR-7598640/darwinbuild/darwinbuild.in:703-731


Property changes on: trunk/darwinbuild/darwinmaster.in
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388/darwinbuild/darwinmaster.sh:399-419
/branches/PR-6358021/darwinbuild/darwinmaster.sh:442-443
/branches/PR-6392966/darwinbuild/darwinmaster.sh:423-427
/branches/PR-6398060/darwinbuild/darwinmaster.sh:433-434
/branches/PR-6493844/darwinbuild/darwinmaster.sh.in:460-461
/branches/PR-6497694/darwinbuild/darwinmaster.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/darwinmaster.in:632-650
/branches/PR-6688645/darwinbuild/darwinmaster.sh.in:479-490
/branches/PR-6722857/darwinbuild/darwinmaster.sh.in:495-499
/branches/PR-6729491/darwinbuild/darwinmaster.in:655-664
/branches/PR-7250612/darwinbuild/darwinmaster.in:635-650
/branches/PR-7341154/darwinbuild/darwinmaster.in:682-694
/branches/PR-7431723/darwinbuild/darwinmaster.in:660-664
/branches/PR-7461534/darwinbuild/darwinmaster.in:650-664
/branches/PR-7489777/darwinbuild/darwinmaster.in:676-731
/branches/PR-7529688/darwinbuild/darwinmaster.in:692-694
/branches/PR-7598640/darwinbuild/darwinmaster.in:703-731
/trunk/darwinbuild/darwinmaster.sh:432-434
   + /branches/PR-4841388/darwinbuild/darwinmaster.sh:399-419
/branches/PR-6358021/darwinbuild/darwinmaster.sh:442-443
/branches/PR-6392966/darwinbuild/darwinmaster.sh:423-427
/branches/PR-6398060/darwinbuild/darwinmaster.sh:433-434
/branches/PR-6493844/darwinbuild/darwinmaster.sh.in:460-461
/branches/PR-6497694/darwinbuild/darwinmaster.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/darwinmaster.in:632-650
/branches/PR-6688645/darwinbuild/darwinmaster.sh.in:479-490
/branches/PR-6722857/darwinbuild/darwinmaster.sh.in:495-499
/branches/PR-6729491/darwinbuild/darwinmaster.in:655-664
/branches/PR-7250612/darwinbuild/darwinmaster.in:635-650
/branches/PR-7341154/darwinbuild/darwinmaster.in:682-694
/branches/PR-7431723/darwinbuild/darwinmaster.in:660-664
/branches/PR-7461534/darwinbuild/darwinmaster.in:650-664
/branches/PR-7489777/darwinbuild/darwinmaster.in:676-731
/branches/PR-7529688/darwinbuild/darwinmaster.in:692-694
/branches/PR-7593824/darwinbuild/darwinmaster.in:739-772
/branches/PR-7598640/darwinbuild/darwinmaster.in:703-731


Property changes on: trunk/darwinbuild/installXcode.in
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388/darwinbuild/installXcode:399-419
/branches/PR-6358021/darwinbuild/installXcode:442-443
/branches/PR-6392966/darwinbuild/installXcode:423-427
/branches/PR-6398060/darwinbuild/installXcode:433-434
/branches/PR-6493844/darwinbuild/installXcode.in:460-461
/branches/PR-6497694/darwinbuild/installXcode.in:466-468,471
/branches/PR-6634286/darwinbuild/installXcode.in:632-650
/branches/PR-6688645/darwinbuild/installXcode.in:479-490
/branches/PR-6722857/darwinbuild/installXcode.in:495-499
/branches/PR-6729491/darwinbuild/installXcode.in:655-664
/branches/PR-7250612/darwinbuild/installXcode.in:635-650
/branches/PR-7341154/darwinbuild/installXcode.in:682-694
/branches/PR-7431723/darwinbuild/installXcode.in:660-664
/branches/PR-7461534/darwinbuild/installXcode.in:650-664
/branches/PR-7489777/darwinbuild/installXcode.in:676-731
/branches/PR-7529688/darwinbuild/installXcode.in:692-694
/branches/PR-7598640/darwinbuild/installXcode.in:703-731
/trunk/darwinbuild/installXcode:432-434
   + /branches/PR-4841388/darwinbuild/installXcode:399-419
/branches/PR-6358021/darwinbuild/installXcode:442-443
/branches/PR-6392966/darwinbuild/installXcode:423-427
/branches/PR-6398060/darwinbuild/installXcode:433-434
/branches/PR-6493844/darwinbuild/installXcode.in:460-461
/branches/PR-6497694/darwinbuild/installXcode.in:466-468,471
/branches/PR-6634286/darwinbuild/installXcode.in:632-650
/branches/PR-6688645/darwinbuild/installXcode.in:479-490
/branches/PR-6722857/darwinbuild/installXcode.in:495-499
/branches/PR-6729491/darwinbuild/installXcode.in:655-664
/branches/PR-7250612/darwinbuild/installXcode.in:635-650
/branches/PR-7341154/darwinbuild/installXcode.in:682-694
/branches/PR-7431723/darwinbuild/installXcode.in:660-664
/branches/PR-7461534/darwinbuild/installXcode.in:650-664
/branches/PR-7489777/darwinbuild/installXcode.in:676-731
/branches/PR-7529688/darwinbuild/installXcode.in:692-694
/branches/PR-7593824/darwinbuild/installXcode.in:739-772
/branches/PR-7598640/darwinbuild/installXcode.in:703-731


Property changes on: trunk/darwinbuild/packageRoots.in
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388/darwinbuild/packageRoots.sh:399-419
/branches/PR-6358021/darwinbuild/packageRoots.sh:442-443
/branches/PR-6392966/darwinbuild/packageRoots.sh:423-427
/branches/PR-6398060/darwinbuild/packageRoots.sh:433-434
/branches/PR-6493844/darwinbuild/packageRoots.sh.in:460-461
/branches/PR-6497694/darwinbuild/packageRoots.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/packageRoots.in:632-650
/branches/PR-6688645/darwinbuild/packageRoots.sh.in:479-490
/branches/PR-6722857/darwinbuild/packageRoots.sh.in:495-499
/branches/PR-6729491/darwinbuild/packageRoots.in:655-664
/branches/PR-7250612/darwinbuild/packageRoots.in:635-650
/branches/PR-7341154/darwinbuild/packageRoots.in:682-694
/branches/PR-7431723/darwinbuild/packageRoots.in:660-664
/branches/PR-7461534/darwinbuild/packageRoots.in:650-664
/branches/PR-7489777/darwinbuild/packageRoots.in:676-731
/branches/PR-7529688/darwinbuild/packageRoots.in:692-694
/branches/PR-7598640/darwinbuild/packageRoots.in:703-731
/trunk/darwinbuild/packageRoots.sh:432-434
   + /branches/PR-4841388/darwinbuild/packageRoots.sh:399-419
/branches/PR-6358021/darwinbuild/packageRoots.sh:442-443
/branches/PR-6392966/darwinbuild/packageRoots.sh:423-427
/branches/PR-6398060/darwinbuild/packageRoots.sh:433-434
/branches/PR-6493844/darwinbuild/packageRoots.sh.in:460-461
/branches/PR-6497694/darwinbuild/packageRoots.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/packageRoots.in:632-650
/branches/PR-6688645/darwinbuild/packageRoots.sh.in:479-490
/branches/PR-6722857/darwinbuild/packageRoots.sh.in:495-499
/branches/PR-6729491/darwinbuild/packageRoots.in:655-664
/branches/PR-7250612/darwinbuild/packageRoots.in:635-650
/branches/PR-7341154/darwinbuild/packageRoots.in:682-694
/branches/PR-7431723/darwinbuild/packageRoots.in:660-664
/branches/PR-7461534/darwinbuild/packageRoots.in:650-664
/branches/PR-7489777/darwinbuild/packageRoots.in:676-731
/branches/PR-7529688/darwinbuild/packageRoots.in:692-694
/branches/PR-7593824/darwinbuild/packageRoots.in:739-772
/branches/PR-7598640/darwinbuild/packageRoots.in:703-731


Property changes on: trunk/darwinbuild/thinPackages.in
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/PR-4841388/darwinbuild/thinPackages.sh:399-419
/branches/PR-6358021/darwinbuild/thinPackages.sh:442-443
/branches/PR-6392966/darwinbuild/thinPackages.sh:423-427
/branches/PR-6398060/darwinbuild/thinPackages.sh:433-434
/branches/PR-6493844/darwinbuild/thinPackages.sh.in:460-461
/branches/PR-6497694/darwinbuild/thinPackages.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/thinPackages.in:632-650
/branches/PR-6688645/darwinbuild/thinPackages.sh.in:479-490
/branches/PR-6722857/darwinbuild/thinPackages.sh.in:495-499
/branches/PR-6729491/darwinbuild/thinPackages.in:655-664
/branches/PR-7250612/darwinbuild/thinPackages.in:635-650
/branches/PR-7341154/darwinbuild/thinPackages.in:682-694
/branches/PR-7431723/darwinbuild/thinPackages.in:660-664
/branches/PR-7461534/darwinbuild/thinPackages.in:650-664
/branches/PR-7489777/darwinbuild/thinPackages.in:676-731
/branches/PR-7529688/darwinbuild/thinPackages.in:692-694
/branches/PR-7598640/darwinbuild/thinPackages.in:703-731
/trunk/darwinbuild/thinPackages.sh:432-434
   + /branches/PR-4841388/darwinbuild/thinPackages.sh:399-419
/branches/PR-6358021/darwinbuild/thinPackages.sh:442-443
/branches/PR-6392966/darwinbuild/thinPackages.sh:423-427
/branches/PR-6398060/darwinbuild/thinPackages.sh:433-434
/branches/PR-6493844/darwinbuild/thinPackages.sh.in:460-461
/branches/PR-6497694/darwinbuild/thinPackages.sh.in:466-468,471
/branches/PR-6634286/darwinbuild/thinPackages.in:632-650
/branches/PR-6688645/darwinbuild/thinPackages.sh.in:479-490
/branches/PR-6722857/darwinbuild/thinPackages.sh.in:495-499
/branches/PR-6729491/darwinbuild/thinPackages.in:655-664
/branches/PR-7250612/darwinbuild/thinPackages.in:635-650
/branches/PR-7341154/darwinbuild/thinPackages.in:682-694
/branches/PR-7431723/darwinbuild/thinPackages.in:660-664
/branches/PR-7461534/darwinbuild/thinPackages.in:650-664
/branches/PR-7489777/darwinbuild/thinPackages.in:676-731
/branches/PR-7529688/darwinbuild/thinPackages.in:692-694
/branches/PR-7593824/darwinbuild/thinPackages.in:739-772
/branches/PR-7598640/darwinbuild/thinPackages.in:703-731

Modified: trunk/darwinup/Archive.cpp
===================================================================
--- trunk/darwinup/Archive.cpp	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Archive.cpp	2010-03-12 22:20:40 UTC (rev 773)
@@ -53,11 +53,13 @@
 	m_date_installed = time(NULL);
 }
 
-Archive::Archive(uint64_t serial, uuid_t uuid, const char* name, const char* path, uint64_t info, time_t date_installed) {
+Archive::Archive(uint64_t serial, uuid_t uuid, const char* name, const char* path, 
+				 uint64_t info, time_t date_installed, const char* build) {
 	m_serial = serial;
 	uuid_copy(m_uuid, uuid);
 	m_name = name ? strdup(name) : NULL;
 	m_path = path ? strdup(path) : NULL;
+	m_build = build ? strdup(build) : NULL;
 	m_info = info;
 	m_date_installed = date_installed;
 }
@@ -66,12 +68,14 @@
 Archive::~Archive() {
 	if (m_path) free(m_path);
 	if (m_name) free(m_name);
+	if (m_build) free(m_build);
 }
 
 uint64_t	Archive::serial()		{ return m_serial; }
 uint8_t*	Archive::uuid()			{ return m_uuid; }
 const char*	Archive::name()			{ return m_name; }
 const char*	Archive::path()			{ return m_path; }
+const char*	Archive::build()		{ return m_build; }
 uint64_t	Archive::info()			{ return m_info; }
 time_t		Archive::date_installed()	{ return m_date_installed; }
 

Modified: trunk/darwinup/Archive.h
===================================================================
--- trunk/darwinup/Archive.h	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Archive.h	2010-03-12 22:20:40 UTC (rev 773)
@@ -97,13 +97,16 @@
 	// Do not modify or free(3).
 	virtual const char* path();
 	
+	// the OS build this archive was installed onto
+	virtual const char* build();
+	
 	// ARCHIVE_INFO flags.
 	virtual uint64_t info();
 	
 	// The epoch seconds when the archive was installed.
 	virtual time_t date_installed();
-
-
+	
+	
 	////
 	//  Member functions
 	////
@@ -130,13 +133,16 @@
 
 	protected:
 
-	// Constructor for subclasses and Depot to use when unserializing an archive from the database.
-	Archive(uint64_t serial, uuid_t uuid, const char* name, const char* path, uint64_t info, time_t date_installed);
+	// Constructor for subclasses and Depot to use when 
+	//  unserializing an archive from the database.
+	Archive(uint64_t serial, uuid_t uuid, const char* name, const char* path, 
+			uint64_t info, time_t date_installed, const char* build);
 	
 	uint64_t	m_serial;
 	uuid_t		m_uuid;
 	char*		m_name;
 	char*		m_path;
+	char*       m_build;
 	uint64_t	m_info;
 	time_t		m_date_installed;
 	

Modified: trunk/darwinup/Column.cpp
===================================================================
--- trunk/darwinup/Column.cpp	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Column.cpp	2010-03-12 22:20:40 UTC (rev 773)
@@ -39,25 +39,30 @@
 Column::Column(const char* name, uint32_t type) {
 	m_name       = strdup(name);
 	m_create_sql = NULL;
+	m_alter_sql = NULL;
 	m_type       = type;
 	m_is_index   = false;
 	m_is_pk      = false;
 	m_is_unique  = false;
+	m_version    = 0;
 }
 
 Column::Column(const char* name, uint32_t type, 
 			   bool is_index, bool is_pk, bool is_unique) {
 	m_name       = strdup(name);
 	m_create_sql = NULL;
+	m_alter_sql = NULL;
 	m_type       = type;
 	m_is_index   = is_index;
 	m_is_pk      = is_pk;
 	m_is_unique  = is_unique;
+	m_version    = 0;
 }
 
 Column::~Column() {
 	free(m_name);
 	free(m_create_sql);
+	free(m_alter_sql);
 }
 
 const char* Column::name() {
@@ -80,6 +85,9 @@
 	return m_is_unique;
 }
 
+uint32_t Column::version() {
+	return m_version;
+}
 
 const char* Column::typestr() {
 	switch(m_type) {
@@ -117,3 +125,12 @@
 	}
 	return (const char*)m_create_sql;
 }
+
+const char* Column::alter(const char* table_name) {
+	if (!m_alter_sql) {
+		asprintf(&m_alter_sql, "ALTER TABLE %s ADD COLUMN %s;",
+				table_name, this->create());
+	}
+	return (const char*)m_alter_sql;
+}
+

Modified: trunk/darwinup/Column.h
===================================================================
--- trunk/darwinup/Column.h	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Column.h	2010-03-12 22:20:40 UTC (rev 773)
@@ -55,6 +55,8 @@
 	const bool     is_pk();
 	const bool     is_unique();
 
+	uint32_t       version();
+	
 	// return size of this column when packed into a result record
 	uint32_t       size();
 
@@ -68,9 +70,13 @@
 	
 	// generate the sql needed to create this column
 	const char*    create();
+	// generate alter table sql for this column for table named table_name
+	const char*    alter(const char* table_name);
 
 	char*          m_name;
-	char*          m_create_sql;
+	uint32_t       m_version; // schema version this was added
+	char*          m_create_sql; // sql fragment for use in CREATE TABLE
+	char*          m_alter_sql; // entire ALTER TABLE ADD COLUMN sql
 	uint32_t       m_type; // SQLITE_* type definition
 	bool           m_is_index;
 	bool           m_is_pk;

Modified: trunk/darwinup/DB.cpp
===================================================================
--- trunk/darwinup/DB.cpp	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/DB.cpp	2010-03-12 22:20:40 UTC (rev 773)
@@ -35,23 +35,32 @@
 
 DarwinupDatabase::DarwinupDatabase(const char* path) : Database(path) {
 	this->connect();
+	this->last_archive = NULL;
 }
 
 DarwinupDatabase::~DarwinupDatabase() {
 	// parent automatically deallocates schema objects
+
+	if (this->last_archive) delete this->last_archive;
 }
 
-void DarwinupDatabase::init_schema() {
+int DarwinupDatabase::init_schema() {
+	
+
+	SCHEMA_VERSION(0);
+	
 	this->m_archives_table = new Table("archives");
+	ADD_TABLE(this->m_archives_table);
 	ADD_PK(m_archives_table, "serial");
 	ADD_INDEX(m_archives_table, "uuid", TYPE_BLOB, true); 
 	ADD_TEXT(m_archives_table, "name");
 	ADD_INTEGER(m_archives_table, "date_added");
 	ADD_INTEGER(m_archives_table, "active");
 	ADD_INTEGER(m_archives_table, "info");	
-	assert(this->add_table(this->m_archives_table)==0);
+
 	
 	this->m_files_table = new Table("files");
+	ADD_TABLE(this->m_files_table);
 	ADD_PK(m_files_table, "serial");
 	ADD_INDEX(m_files_table, "archive", TYPE_INTEGER, false);
 	ADD_INTEGER(m_files_table, "info");
@@ -61,10 +70,17 @@
 	ADD_INTEGER(m_files_table, "size");
 	ADD_BLOB(m_files_table, "digest");
 	ADD_INDEX(m_files_table, "path", TYPE_TEXT, false);
+	
 	// custom index to protect from duplicate files
 	assert(this->m_files_table->set_custom_create("CREATE UNIQUE INDEX files_archive_path " 
 												  "ON files (archive, path);") == 0);
-	assert(this->add_table(this->m_files_table)==0);
+
+
+	SCHEMA_VERSION(1);
+
+	ADD_TEXT(m_archives_table, "osbuild");
+	
+	return 0;
 }
 
 int DarwinupDatabase::activate_archive(uint64_t serial) {
@@ -78,6 +94,7 @@
 }
 
 int DarwinupDatabase::set_archive_active(uint64_t serial, uint64_t* active) {
+	this->clear_last_archive();
 	return this->update_value("activate_archive", 
 							  this->m_archives_table,
 							  this->m_archives_table->column(4), // active
@@ -89,6 +106,7 @@
 
 int DarwinupDatabase::update_archive(uint64_t serial, uuid_t uuid, const char* name,
 									  time_t date_added, uint32_t active, uint32_t info) {
+	this->clear_last_archive();
 	return this->update(this->m_archives_table, serial,
 						(uint8_t*)uuid,
 						(uint32_t)sizeof(uuid_t),
@@ -99,7 +117,7 @@
 }
 
 uint64_t DarwinupDatabase::insert_archive(uuid_t uuid, uint32_t info, const char* name, 
-										  time_t date_added) {
+										  time_t date_added, const char* build) {
 	
 	int res = this->insert(this->m_archives_table,
 						   (uint8_t*)uuid,
@@ -107,7 +125,8 @@
 						   name,
 						   (uint64_t)date_added,
 						   (uint64_t)0,
-						   (uint64_t)info);
+						   (uint64_t)info,
+						   build);
 	if (res != SQLITE_OK) {
 		fprintf(stderr, "Error: unable to insert archive %s: %s \n",
 				name, this->error());
@@ -146,17 +165,23 @@
 	char* path;
 	memcpy(&path, &data[this->file_offset(8)], sizeof(char*));
 	
-	uint8_t* archive_data;
-	int res = this->get_archive(&archive_data, archive_serial);
-	Archive* archive = NULL;
-	if (FOUND(res)) {
-		archive = this->make_archive(archive_data);
+	// get archive, which may be stored in last_archive
+	int res = DB_OK;
+	bool cached = false;
+	Archive* archive = this->get_last_archive(archive_serial);
+	if (archive) {
+		cached = true;
 	} else {
+		uint8_t* archive_data;
+		res = this->get_archive(&archive_data, archive_serial);
+		this->set_last_archive(archive_data);
+		archive = this->last_archive;
+	}
+	if (!archive) {
 		fprintf(stderr, "Error: DB::make_file could not find the archive for file: %s: %d \n", path, res);
 		return NULL;
 	}
-	this->m_archives_table->free_result(archive_data);
-	
+
 	File* result = FileFactory(serial, archive, info, (const char*)path, mode, uid, gid, size, digest);
 	this->m_files_table->free_result(data);
 	
@@ -318,6 +343,10 @@
 	return DB_OK;
 }
 
+int DarwinupDatabase::free_archive(uint8_t* data) {
+	return this->m_archives_table->free_result(data);
+}
+
 int DarwinupDatabase::delete_file(File* file) {
 	int res = this->del(this->m_files_table, file->serial());
 	if (res != SQLITE_OK) return DB_ERROR;
@@ -340,6 +369,9 @@
 	return DB_OK;
 }
 
+int DarwinupDatabase::free_file(uint8_t* data) {
+	return this->m_files_table->free_result(data);
+}
 
 int DarwinupDatabase::get_inactive_archive_serials(uint64_t** serials, uint32_t* count) {
 	int res = this->get_column("inactive_archive_serials",
@@ -392,9 +424,12 @@
 	memcpy(&date_added, &data[this->archive_offset(3)], sizeof(time_t));
 	uint64_t info;
 	memcpy(&info, &data[this->archive_offset(5)], sizeof(uint64_t));
+	char* build;
+	memcpy(&build, &data[this->archive_offset(6)], sizeof(char*));
 
-	Archive* archive = new Archive(serial, *uuid, name, NULL, info, date_added);
+	Archive* archive = new Archive(serial, *uuid, name, NULL, info, date_added, build);
 	this->m_archives_table->free_result(data);
+
 	return archive;
 }
 
@@ -402,7 +437,7 @@
 	int res = this->get_all_ordered("get_archives",
 									data, count,
 									this->m_archives_table,
-									this->m_archives_table->column(0), // order by path
+									this->m_archives_table->column(0), // order by serial
 									ORDER_BY_DESC,
 									1,
 									this->m_archives_table->column(2),  // name
@@ -425,7 +460,6 @@
 	return DB_ERROR;	
 }
 
-// XXX: get_archive gets called repeatedly by make_file, should memoize
 int DarwinupDatabase::get_archive(uint8_t** data, uint64_t serial) {
 	int res = this->get_row("archive__serial",
 							data,
@@ -433,7 +467,9 @@
 							1,
 							this->m_archives_table->column(0), // serial
 							'=', serial);
-	if (res == SQLITE_ROW) return (DB_FOUND | DB_OK);
+	if (res == SQLITE_ROW) {
+		return (DB_FOUND | DB_OK);
+	}
 	if (res == SQLITE_DONE) return DB_OK;
 	return DB_ERROR;	
 }
@@ -453,12 +489,14 @@
 int DarwinupDatabase::get_archive(uint8_t** data, archive_keyword_t keyword) {
 	int res = SQLITE_OK;
 	int order = ORDER_BY_DESC;
+	const char* name = "archive_newest";
 
 	if (keyword == DEPOT_ARCHIVE_OLDEST) {
 		order = ORDER_BY_ASC;
+		name = "archive_oldest";
 	}
 	
-	res = this->get_row_ordered("archive__keyword",
+	res = this->get_row_ordered(name,
 								data,
 								this->m_archives_table,
 								this->m_archives_table->column(3), // order by date_added
@@ -479,3 +517,22 @@
 int DarwinupDatabase::file_offset(int column) {
 	return this->m_files_table->offset(column);
 }
+
+Archive* DarwinupDatabase::get_last_archive(uint64_t serial) {
+	if (this->last_archive && this->last_archive->serial() == serial) {
+		return this->last_archive;
+	}
+	return NULL;
+}
+
+int DarwinupDatabase::clear_last_archive() {
+	delete this->last_archive;
+	this->last_archive = NULL;
+	return 0;
+}
+
+int DarwinupDatabase::set_last_archive(uint8_t* data) {
+	this->last_archive = this->make_archive(data);
+	if (this->last_archive) return 0;
+	return 1;
+}

Modified: trunk/darwinup/DB.h
===================================================================
--- trunk/darwinup/DB.h	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/DB.h	2010-03-12 22:20:40 UTC (rev 773)
@@ -56,7 +56,7 @@
 struct DarwinupDatabase : Database {
 	DarwinupDatabase(const char* path);
 	virtual ~DarwinupDatabase();
-	void init_schema();
+	int init_schema();
 	
 	uint64_t count_files(Archive* archive, const char* path);
 	uint64_t count_archives(bool include_rollbacks);
@@ -74,16 +74,19 @@
 	int      deactivate_archive(uint64_t serial);
 	int      update_archive(uint64_t serial, uuid_t uuid, const char* name,
 							time_t date_added, uint32_t active, uint32_t info);
-	uint64_t insert_archive(uuid_t uuid, uint32_t info, const char* name, time_t date);
+	uint64_t insert_archive(uuid_t uuid, uint32_t info, const char* name, 
+							time_t date, const char* build);
 	int      delete_empty_archives();
 	int      delete_archive(Archive* archive);
 	int      delete_archive(uint64_t serial);
+	int      free_archive(uint8_t* data);
 
 	// Files
 	File*    make_file(uint8_t* data);
 	int      get_next_file(uint8_t** data, File* file, file_starseded_t star);
 	int      get_file_serials(uint64_t** serials, uint32_t* count);
-	int      get_file_serial_from_archive(Archive* archive, const char* path, uint64_t** serial);
+	int      get_file_serial_from_archive(Archive* archive, const char* path, 
+										  uint64_t** serial);
 	int      get_files(uint8_t*** data, uint32_t* count, Archive* archive);
 	int      file_offset(int column);
 	int      update_file(uint64_t serial, Archive* archive, uint32_t info, mode_t mode, 
@@ -93,7 +96,13 @@
 	int      delete_file(uint64_t serial);
 	int      delete_file(File* file);
 	int      delete_files(Archive* archive);
+	int      free_file(uint8_t* data);
 	
+	// memoization
+	Archive* get_last_archive(uint64_t serial);
+	int      clear_last_archive();
+	int      set_last_archive(uint8_t* data);
+	
 
 protected:
 	
@@ -102,6 +111,9 @@
 	Table*        m_archives_table;
 	Table*        m_files_table;
 	
+	// memoize some get_archive calls
+	Archive*      last_archive;
+	
 };
 
 #endif

Modified: trunk/darwinup/Database.cpp
===================================================================
--- trunk/darwinup/Database.cpp	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Database.cpp	2010-03-12 22:20:40 UTC (rev 773)
@@ -36,10 +36,11 @@
  * sqlite3_trace callback for debugging
  */
 void dbtrace(void* context, const char* sql) {
-	IF_DEBUG("[TRACE] %s \n", sql);
+	fprintf(stderr, "[SQL] %s\n", sql);
 }
 
 Database::Database() {
+	m_schema_version = 0;
 	m_table_max = 2;
 	m_table_count = 0;
 	m_tables = (Table**)malloc(sizeof(Table*) * m_table_max);
@@ -51,6 +52,7 @@
 }
 
 Database::Database(const char* path) {
+	m_schema_version = 0;
 	m_table_max = 2;
 	m_table_count = 0;
 	m_tables = (Table**)malloc(sizeof(Table*) * m_table_max);
@@ -80,11 +82,24 @@
 	free(m_error);
 }
 
+uint32_t Database::schema_version() {
+	return this->m_schema_version;
+}
 
-void Database::init_schema() {
+void Database::schema_version(uint32_t v) {
+	this->m_schema_version = v;
+}
+
+int Database::init_schema() {
 	// do nothing... children should implement this
+	return DB_OK;
 }
 
+int Database::post_table_creation() {
+	// clients can implement this
+	return DB_OK;
+}
+
 const char* Database::path() {
 	return m_path;
 }
@@ -94,12 +109,20 @@
 }
 
 int Database::connect() {
+	int res = DB_OK;
+	
 	if (!m_path) {
 		fprintf(stderr, "Error: need to specify a path to Database.\n");
 		return -1;
 	}
-	int res = SQLITE_OK;
-	this->init_schema();
+
+	res = this->pre_connect();
+	if (res) {
+		fprintf(stderr, "Error: pre-connection failed.\n");
+		return res;
+	}
+	
+	int exists = is_regular_file(m_path);
 	res = sqlite3_open(m_path, &m_db);
 	if (res) {
 		sqlite3_close(m_db);
@@ -107,24 +130,67 @@
 		fprintf(stderr, "Error: unable to connect to database at: %s \n", 
 				m_path);
 		return res;
-	}	
-	sqlite3_trace(m_db, dbtrace, NULL);
-	if (this->is_empty()) {
+	}
+	
+	res = this->post_connect();
+	if (res) {
+		fprintf(stderr, "Error: post-connection failed.\n");
+		return res;
+	}
+	
+	if (!exists) {
+		// create schema since it is empty
 		assert(this->create_tables() == 0);
+		assert(this->set_schema_version(this->m_schema_version) == 0);
+	} else {
+		// test schema versions
+		uint32_t version = 0;
+		if (this->has_information_table()) {
+			version = this->get_schema_version();
+		}
+
+		if (version < this->m_schema_version) {
+			IF_DEBUG("Upgrading schema from %u to %u \n", version, this->m_schema_version);
+			assert(this->upgrade_schema(version) == 0);
+			assert(this->set_schema_version(this->m_schema_version) == 0);
+		}
+		if (version > this->m_schema_version) {
+			fprintf(stderr, "Error: this client is too old!\n");
+			return DB_ERROR;
+		}
 	}
 	
+	return res;	
+}
+
+int Database::pre_connect() {	
+	int res = DB_OK;
+	res = this->init_internal_schema();
+	res = this->init_schema();
+	return res;
+}
+
+int Database::post_connect() {
+	int res = DB_OK;
+	
 	// prepare transaction statements
-	if (res == SQLITE_OK) 
+	if (res == DB_OK) 
 		res = sqlite3_prepare_v2(m_db, "BEGIN TRANSACTION", 18,
 								 &m_begin_transaction, NULL);
-	if (res == SQLITE_OK) 
+	if (res == DB_OK) 
 		res = sqlite3_prepare_v2(m_db, "ROLLBACK TRANSACTION", 21,
 								 &m_rollback_transaction, NULL);
-	if (res == SQLITE_OK) 
+	if (res == DB_OK) 
 		res = sqlite3_prepare_v2(m_db, "COMMIT TRANSACTION", 19,
-								 &m_commit_transaction, NULL);
-	
-	return res;	
+								 &m_commit_transaction, NULL);	
+
+	// debug settings
+	extern uint32_t verbosity;
+	if (verbosity & VERBOSE_SQL) {
+		sqlite3_trace(m_db, dbtrace, NULL);
+	}
+		
+	return res;
 }
 
 int Database::connect(const char* path) {
@@ -550,6 +616,7 @@
 		m_table_max *= REALLOC_FACTOR;
 	}
 	m_tables[m_table_count++] = t;
+	t->m_version = this->m_schema_version;
 	
 	return 0;
 }
@@ -571,6 +638,16 @@
 	return res != SQLITE_OK;
 }
 
+int Database::create_table(Table* table) {
+	int res = this->sql_once(table->create());
+	if (res != DB_OK) {
+		fprintf(stderr, "Error: sql error trying to create"
+				" table: %s: %s\n",
+				table->name(), m_error);
+	}
+	return res;
+}
+
 int Database::create_tables() {
 	int res = SQLITE_OK;
 	for (uint32_t i=0; i<m_table_count; i++) {
@@ -581,9 +658,150 @@
 			return res;
 		}
 	}
+	if (res == DB_OK) res = this->initial_data();
+	if (res == DB_OK) res = this->post_table_creation();
 	return res;
 }
 
+int Database::upgrade_schema(uint32_t version) {
+	int res = DB_OK;
+	this->begin_transaction();
+	
+	res = this->upgrade_internal_schema(version);
+	if (res != DB_OK) {
+		fprintf(stderr, "Error: unable to upgrade internal schema.\n");
+		this->rollback_transaction();
+		return res;
+	}			
+	
+	for (uint32_t ti = 0; res == DB_OK && ti < m_table_count; ti++) {
+		if (m_tables[ti]->version() > version) {
+			// entire table is new
+			res = this->create_table(m_tables[ti]);
+		} else {
+			// table is same version, so check for new columns
+			for (uint32_t ci = 0; res == DB_OK && ci < m_tables[ti]->column_count(); ci++) {
+				if (m_tables[ti]->column(ci)->version() < version) {
+					// this should never happen
+					fprintf(stderr, "Error: internal error with schema versioning."
+									" Column %s is older than its table %s. \n",
+							m_tables[ti]->column(ci)->name(), m_tables[ti]->name());
+				}
+				if (m_tables[ti]->column(ci)->version() > version) {
+					// column is new
+					res = this->sql_once(m_tables[ti]->alter_add_column(ci));
+					if (res != DB_OK) {
+						fprintf(stderr, "Error: sql error trying to upgrade (alter)"
+						        " table: %s column: %s : %s\n",
+								m_tables[ti]->name(), m_tables[ti]->column(ci)->name(), 
+								m_error);
+					}		
+				}
+			}
+		}
+	}
+	
+	if (res == DB_OK) {
+		this->commit_transaction();
+	} else {
+		this->rollback_transaction();
+	}
+
+	return res;
+}
+
+int Database::upgrade_internal_schema(uint32_t version) {
+	int res = DB_OK;
+	
+	if (version == 0) {
+		res = this->sql_once(this->m_information_table->create());
+	}
+	
+	return res;
+}
+
+int Database::init_internal_schema() {
+	this->m_information_table = new Table("database_information");
+	ADD_TABLE(this->m_information_table);
+	ADD_PK(m_information_table, "id");
+	ADD_INDEX(m_information_table, "variable", TYPE_TEXT, true);
+	ADD_TEXT(m_information_table, "value");
+	return DB_OK;
+}
+
+int Database::initial_data() {
+	// load our initial config data
+	return this->insert(m_information_table, "schema_version", "0");
+}
+
+int Database::get_information_value(const char* variable, char*** value) {
+	return this->get_value("get_information_value",
+						   (void**)value,
+						   this->m_information_table,
+						   this->m_information_table->column(2), // value
+						   1,
+						   this->m_information_table->column(1), // variable
+						   '=', variable);
+}
+
+int Database::update_information_value(const char* variable, const char* value) {
+	int res = SQLITE_OK;
+	uint64_t* c;
+	res = this->count("count_info_var",
+					  (void**)&c,
+					  this->m_information_table,
+					  1,
+					  this->m_information_table->column(1), // variable
+					  '=', variable);
+					  
+	if (*c > 0) {
+		res = this->update_value("update_information_value",
+								  this->m_information_table,
+								  this->m_information_table->column(2), // value
+								  (void**)&value, 
+								  1,
+								  this->m_information_table->column(1), // variable
+								  '=', variable);
+	} else {
+		res = this->insert(m_information_table, variable, value);
+	}
+	
+	return res;
+}
+
+bool Database::has_information_table() {
+	int res = sqlite3_exec(this->m_db, 
+						   "SELECT count(*) FROM database_information;", 
+						   NULL, NULL, NULL);
+	return res == SQLITE_OK;
+}
+
+uint32_t Database::get_schema_version() {
+	int res = SQLITE_OK;
+	char** vertxt = NULL;
+	res = this->get_information_value("schema_version", &vertxt);
+	if (res == SQLITE_ROW) {
+		uint32_t version = (uint32_t)strtoul(*vertxt, NULL, 10);
+		free(*vertxt);
+		return version;
+	} else {
+		// lack of information table/value means we are 
+		//  upgrading an old database
+		return 0;
+	}
+}
+
+int Database::set_schema_version(uint32_t version) {
+	IF_DEBUG("set_schema_version %u \n", version);
+	int res = DB_OK;
+	char* vertxt;
+	asprintf(&vertxt, "%u", version);
+	if (!vertxt) return DB_ERROR;
+	res = this->update_information_value("schema_version", vertxt);
+	free(vertxt);
+	return res;
+}
+
 size_t Database::store_column(sqlite3_stmt* stmt, int column, uint8_t* output) {
 	size_t used;
 	int type = sqlite3_column_type(stmt, column);
@@ -643,7 +861,7 @@
 			*used = current - output;
 		}
 	}
-	
+
 	return res;
 }
 

Modified: trunk/darwinup/Database.h
===================================================================
--- trunk/darwinup/Database.h	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Database.h	2010-03-12 22:20:40 UTC (rev 773)
@@ -67,19 +67,21 @@
 #define FOUND(x)  ((x & DB_FOUND) && !(x & DB_ERROR))
 
 // Schema creation macros
+#define SCHEMA_VERSION(v) this->schema_version(v);
+#define ADD_TABLE(t) assert(this->add_table(t)==0);
 #define ADD_COLUMN(table, name, type, index, pk, unique) \
-    assert(table->add_column(new Column(name, type, index, pk, unique))==0);
+    assert(table->add_column(new Column(name, type, index, pk, unique), this->schema_version())==0);
 #define ADD_INDEX(table, name, type, unique) \
-	assert(table->add_column(new Column(name, type, true, false, unique))==0);
+	assert(table->add_column(new Column(name, type, true, false, unique), this->schema_version())==0);
 #define ADD_PK(table, name) \
 	assert(table->add_column(new Column(name, TYPE_INTEGER, \
-                                        false, true, false))==0);
+                                        false, true, false), this->schema_version())==0);
 #define ADD_TEXT(table, name) \
-	assert(table->add_column(new Column(name, TYPE_TEXT))==0);
+	assert(table->add_column(new Column(name, TYPE_TEXT), this->schema_version())==0);
 #define ADD_INTEGER(table, name) \
-	assert(table->add_column(new Column(name, TYPE_INTEGER))==0);
+	assert(table->add_column(new Column(name, TYPE_INTEGER), this->schema_version())==0);
 #define ADD_BLOB(table, name) \
-	assert(table->add_column(new Column(name, TYPE_BLOB))==0);
+	assert(table->add_column(new Column(name, TYPE_BLOB), this->schema_version())==0);
 
 
 /**
@@ -92,14 +94,22 @@
 	Database(const char* path);
 	virtual ~Database();
 
+	// public setter/getter of class attr
+	uint32_t     schema_version();
+	void         schema_version(uint32_t v);
+	
 	/**
 	 * init_schema is called during db connection.
 	 * Projects implementing a Database derived class
 	 * should use Table::add_column() or the ADD_*
 	 * macros in their init_schema() to define their schema
 	 */
-	virtual void init_schema();
+	virtual int  init_schema();
 	
+	// called after tables are created so clients can load
+	// initial sets of data
+	virtual int  post_table_creation();
+	
 	const char*  path();
 	const char*  error();
 	int          connect();
@@ -168,17 +178,38 @@
 	
 protected:
 
+	// pre- and post- connection work
+	int   pre_connect();
+	int   post_connect();
+	
+	int   upgrade_schema(uint32_t version);
+	int   upgrade_internal_schema(uint32_t version);
+	int   init_internal_schema();
+	int   initial_data();
+
+	int   get_information_value(const char* variable, char*** value);
+	int   update_information_value(const char* variable, const char* value);
+	
+	// get and set version info in actual database
+	bool      has_information_table();
+	uint32_t  get_schema_version();
+	int       set_schema_version(uint32_t version);
+	
 	// execute query with printf-style format, does not cache statement
-	int sql_once(const char* fmt, ...);
+	int   sql_once(const char* fmt, ...);
 	// cache statement with name, execute query with printf-style format
-	int sql(const char* name, const char* fmt, ...);
-	int execute(sqlite3_stmt* stmt);
+	int   sql(const char* name, const char* fmt, ...);
+	int   execute(sqlite3_stmt* stmt);
 	
-	int  add_table(Table*);
+	int   add_table(Table*);
 	
 	// test if database has had its tables created
 	bool  is_empty();
+	// create tables for the client
+	int   create_table(Table* table);
 	int   create_tables();
+	// create tables for ourselves
+	int   create_internal_tables();
 
 	// bind all table columns from va_list
 	int   bind_all_columns(sqlite3_stmt* stmt, Table* table, va_list args);
@@ -201,7 +232,11 @@
 	
 	char*            m_path;
 	sqlite3*         m_db;
-
+	
+	uint32_t         m_schema_version;
+	Table*           m_information_table;
+	sqlite3_stmt*    m_get_information_value;
+	
 	Table**          m_tables;
 	uint32_t         m_table_count;
 	uint32_t         m_table_max;

Modified: trunk/darwinup/Depot.cpp
===================================================================
--- trunk/darwinup/Depot.cpp	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Depot.cpp	2010-03-12 22:20:40 UTC (rev 773)
@@ -39,6 +39,7 @@
 #include <copyfile.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <grp.h>
 #include <libgen.h>
 #include <limits.h>
 #include <stdio.h>
@@ -54,6 +55,7 @@
 	m_database_path = NULL;
 	m_archives_path = NULL;
 	m_downloads_path = NULL;
+	m_build = NULL;
 	m_db = NULL;
 	m_lock_fd = -1;
 	m_is_locked = 0;
@@ -64,6 +66,7 @@
 	m_lock_fd = -1;
 	m_is_locked = 0;
 	m_depot_mode = 0750;
+	m_build = NULL;
 
 	asprintf(&m_prefix, "%s", prefix);
 	join_path(&m_depot_path, m_prefix, "/.DarwinDepot");
@@ -100,18 +103,28 @@
 }
 
 int Depot::create_storage() {
+	uid_t uid = getuid();
+	struct group *gs = getgrnam("admin");
+	gid_t gid = gs->gr_gid;
+	
 	int res = mkdir(m_depot_path, m_depot_mode);
+	res = chmod(m_depot_path, m_depot_mode);
+	res = chown(m_depot_path, uid, gid);
 	if (res && errno != EEXIST) {
 		perror(m_depot_path);
 		return res;
 	}
 	res = mkdir(m_archives_path, m_depot_mode);
+	res = chmod(m_archives_path, m_depot_mode);
+	res = chown(m_archives_path, uid, gid);
 	if (res && errno != EEXIST) {
 		perror(m_archives_path);
 		return res;
 	}
 	
 	res = mkdir(m_downloads_path, m_depot_mode);
+	res = chmod(m_downloads_path, m_depot_mode);
+	res = chown(m_downloads_path, uid, gid);
 	if (res && errno != EEXIST) {
 		perror(m_downloads_path);
 		return res;
@@ -124,7 +137,8 @@
 	int res = 0;
 	
 	// initialization requires all these paths to be set
-	if (!(m_prefix && m_depot_path && m_database_path && m_archives_path && m_downloads_path)) {
+	if (!(m_prefix && m_depot_path && m_database_path && 
+		  m_archives_path && m_downloads_path)) {
 		return -1;
 	}
 	
@@ -138,14 +152,20 @@
 		if (res) return res;
 		res = this->lock(LOCK_SH);
 		if (res) return res;
-		m_is_locked = 1;		
+		m_is_locked = 1;
+		res = build_number_for_path(&m_build, m_prefix);
 	}
 
-	int exists = is_regular_file(m_database_path);
-	if (!exists && !writable) {
-		// read-only mode requested but we have no database
-		return -2;
+	struct stat sb;
+	res = stat(m_database_path, &sb);
+	if (!writable && res == -1 && (errno == ENOENT || errno == ENOTDIR)) {
+		// depot does not exist
+		return -2; 
 	}
+	if (!writable && res == -1 && errno == EACCES) {
+		// permission denied
+		return -3;
+	}
 
 	res = this->connect();
 
@@ -256,7 +276,8 @@
 			if (archive) {
 				list[i] = archive;
 			} else {
-				fprintf(stderr, "%s:%d: DB::make_archive returned NULL\n", __FILE__, __LINE__);
+				fprintf(stderr, "%s:%d: DB::make_archive returned NULL\n",
+						__FILE__, __LINE__);
 				res = -1;
 				break;
 			}
@@ -266,6 +287,37 @@
 	return list;	
 }
 
+Archive** Depot::get_superseded_archives(uint32_t* count) {
+	int res = DB_OK;
+	uint8_t** archlist;
+	res = this->m_db->get_archives(&archlist, count, false); // rollbacks cannot be superseded
+	
+	Archive** list = (Archive**)malloc(sizeof(Archive*) * (*count));
+	if (!list) {
+		fprintf(stderr, "Error: ran out of memory in Depot::get_superseded_archives\n");
+		return NULL;
+	}
+
+	uint32_t i = 0;
+	uint32_t cur = i;
+	if (FOUND(res)) {
+		while (i < *count) {
+			Archive* archive = this->m_db->make_archive(archlist[i++]);
+			if (archive && this->is_superseded(archive)) {
+				list[cur++] = archive;
+			} else if (!archive) {
+				fprintf(stderr, "%s:%d: DB::make_archive returned NULL\n",
+						__FILE__, __LINE__);
+				res = -1;
+				break;
+			}
+		}
+	}
+	// adjust count based on our is_superseded filtering
+	*count = cur;
+	return list;	
+}
+
 uint64_t Depot::count_archives() {
 	extern uint32_t verbosity;
 	uint64_t c = this->m_db->count_archives((bool)(verbosity & VERBOSE_DEBUG));
@@ -308,7 +360,9 @@
 }
 
 
-int Depot::analyze_stage(const char* path, Archive* archive, Archive* rollback, int* rollback_files) {
+int Depot::analyze_stage(const char* path, Archive* archive, Archive* rollback,
+						 int* rollback_files) {
+	extern uint32_t dryrun;
 	int res = 0;
 	assert(archive != NULL);
 	assert(rollback != NULL);
@@ -336,7 +390,6 @@
 			char* actpath;
 			join_path(&actpath, this->prefix(), file->path());
 			File* actual = FileFactory(actpath);
-
 			File* preceding = this->file_preceded_by(file);
 			
 			if (actual == NULL) {
@@ -394,6 +447,16 @@
 					}
 				}				
 			}
+
+			// if file == actual, but actual != preceding, then an external
+			// process changed actual to be the same as what we are installing
+			// now (OS upgrade?). We do not need to save actual, but make
+			// a special state so the user knows what happened and does not
+			// get a ?.
+			if (actual_flags == FILE_INFO_IDENTICAL && preceding_flags != FILE_INFO_IDENTICAL) {
+				IF_DEBUG("[analyze]    external changes but file same as actual\n");
+				state = 'E';
+			}
 			
 			// XXX: should this be done in backup_file?
 			// If we're going to need to squirrel away data, create
@@ -417,9 +480,10 @@
 				join_path(&backup_dirpath, uuidpath, dir);
 				assert(backup_dirpath != NULL);
 				
-				res = mkdir_p(backup_dirpath);
+				if (!dryrun) res = mkdir_p(backup_dirpath);
 				if (res != 0 && errno != EEXIST) {
-					fprintf(stderr, "%s:%d: %s: %s (%d)\n", __FILE__, __LINE__, backup_dirpath, strerror(errno), errno);
+					fprintf(stderr, "%s:%d: %s: %s (%d)\n", 
+							__FILE__, __LINE__, backup_dirpath, strerror(errno), errno);
 				} else {
 					res = 0;
 				}
@@ -433,7 +497,7 @@
 				*rollback_files += 1;
 				if (!this->has_file(rollback, actual)) {
 					IF_DEBUG("[analyze]    insert rollback\n");
-					res = this->insert(rollback, actual);
+					if (!dryrun) res = this->insert(rollback, actual);
 				}
 				assert(res == 0);
 
@@ -455,8 +519,9 @@
 						}
 						
 						if (!this->has_file(rollback, parent)) {
-							IF_DEBUG("[analyze]      adding parent to rollback: %s \n", parent->path());
-							res = this->insert(rollback, parent);
+							IF_DEBUG("[analyze]      adding parent to rollback: %s \n", 
+									 parent->path());
+							if (!dryrun) res = this->insert(rollback, parent);
 						}
 						assert(res == 0);
 						pent = pent->fts_parent;
@@ -465,7 +530,7 @@
 			}
 
 			fprintf(stderr, "%c %s\n", state, file->path());
-			res = this->insert(archive, file);
+			if (!dryrun) res = this->insert(archive, file);
 			assert(res == 0);
 			if (preceding && preceding != actual) delete preceding;
 			if (actual) delete actual;
@@ -543,7 +608,8 @@
 		IF_DEBUG("[backup] copyfile(%s, %s)\n", path, dstpath);
 		res = copyfile(path, dstpath, NULL, COPYFILE_ALL|COPYFILE_NOFOLLOW);
 
-		if (res != 0) fprintf(stderr, "%s:%d: backup failed: %s: %s (%d)\n", __FILE__, __LINE__, dstpath, strerror(errno), errno);
+		if (res != 0) fprintf(stderr, "%s:%d: backup failed: %s: %s (%d)\n", 
+							  __FILE__, __LINE__, dstpath, strerror(errno), errno);
 		free(path);
 		free(dstpath);
 		free(uuidpath);
@@ -563,7 +629,8 @@
 	} else {
 		res = file->install_info(context->depot->m_prefix);
 	}
-	if (res != 0) fprintf(stderr, "%s:%d: install failed: %s: %s (%d)\n", __FILE__, __LINE__, file->path(), strerror(errno), errno);
+	if (res != 0) fprintf(stderr, "%s:%d: install failed: %s: %s (%d)\n", 
+						  __FILE__, __LINE__, file->path(), strerror(errno), errno);
 	return res;
 }
 
@@ -597,9 +664,15 @@
 
 
 int Depot::install(Archive* archive) {
+	extern uint32_t dryrun;
 	int res = 0;
 	Archive* rollback = new RollbackArchive();
 
+	if (this->m_build) {
+		rollback->m_build = strdup(this->m_build);
+		archive->m_build = strdup(this->m_build);
+	}
+	
 	assert(rollback != NULL);
 	assert(archive != NULL);
 
@@ -609,15 +682,15 @@
 	//
 	// The fun starts here
 	//
-	if (res == 0) res = this->begin_transaction();	
+	if (!dryrun && res == 0) res = this->begin_transaction();	
 
 	//
 	// Insert the rollback archive before the new archive to install, thus keeping
 	// the chronology of the serial numbers correct.  We may later choose to delete
 	// the rollback archive if we determine that it was not necessary.
 	//
-	if (res == 0) res = this->insert(rollback);
-	if (res == 0) res = this->insert(archive);
+	if (!dryrun && res == 0) res = this->insert(rollback);
+	if (!dryrun && res == 0) res = this->insert(archive);
 
 	//
 	// Create the stage directory and rollback backing store directories
@@ -627,7 +700,6 @@
 	char* rollback_path = rollback->create_directory(m_archives_path);
 	assert(rollback_path != NULL);
 
-
 	// Extract the archive into its backing store directory
 	if (res == 0) res = archive->extract(archive_path);
 
@@ -637,6 +709,16 @@
 	int rollback_files = 0;
 	if (res == 0) res = this->analyze_stage(archive_path, archive, rollback, &rollback_files);
 	
+	// we can stop now if this is a dry run
+	if (dryrun) {
+		remove_directory(archive_path);
+		remove_directory(rollback_path);
+		free(rollback_path);
+		free(archive_path);
+		(void)this->lock(LOCK_SH);
+		return res;
+	}
+	
 	// If no files were added to the rollback archive, delete the rollback archive.
 	if (res == 0 && rollback_files == 0) {
 		res = this->remove(rollback);
@@ -713,13 +795,30 @@
 	return res;
 }
 
-int Depot::prune_archives() {
+// delete the unexpanded tarball from archives storage
+int Depot::prune_archive(Archive* archive) {
 	int res = 0;
+	
+	// clean up database
 	res = this->m_db->delete_empty_archives();
+	if (res) {
+		fprintf(stderr, "Error: unable to prune archives from database.\n");
+		return res;
+	}
+	
+	// clean up disk
+	char path[PATH_MAX];
+	char uuid[37];
+	uuid_unparse_upper(archive->uuid(), uuid);
+	if (res == 0) snprintf(path, PATH_MAX, "%s/%s.tar.bz2", m_archives_path, uuid);
+	if (res == 0) res = unlink(path);
+	if (res) perror(path);
+	
 	return res;
 }
 
 int Depot::uninstall_file(File* file, void* ctx) {
+	extern uint32_t dryrun;
 	InstallContext* context = (InstallContext*)ctx;
 	int res = 0;
 	char state = ' ';
@@ -737,9 +836,12 @@
 	IF_DEBUG("[uninstall] actual path is %s\n", actpath);
 	File* actual = FileFactory(actpath);
 	uint32_t flags = File::compare(file, actual);
-		
-	if (actual != NULL && flags != FILE_INFO_IDENTICAL) {
-		// XXX: probably not the desired behavior
+	
+	if (actual == NULL) {
+		IF_DEBUG("[uninstall]    actual file missing, "
+				 "possibly due to parent being removed already\n");
+		state = '!';
+	} else if (flags != FILE_INFO_IDENTICAL) {
 		IF_DEBUG("[uninstall]    changes since install; skipping\n");
 	} else {
 		File* superseded = context->depot->file_superseded_by(file);
@@ -750,7 +852,7 @@
 			if (INFO_TEST(preceding->info(), FILE_INFO_NO_ENTRY)) {
 				state = 'R';
 				IF_DEBUG("[uninstall]    removing file\n");
-				if (actual && res == 0) res = actual->remove();
+				if (!dryrun && actual && res == 0) res = actual->remove();
 			} else {
 				// copy the preceding file back out to the system
 				// if it's different from what's already there
@@ -758,11 +860,17 @@
 				if (INFO_TEST(flags, FILE_INFO_DATA_DIFFERS)) {
 					state = 'U';
 					IF_DEBUG("[uninstall]    restoring\n");
-					if (res == 0) res = preceding->install(context->depot->m_archives_path, context->depot->m_prefix);
+					if (!dryrun && res == 0) {
+						res = preceding->install(context->depot->m_archives_path, 
+												 context->depot->m_prefix);
+					}
 				} else if (INFO_TEST(flags, FILE_INFO_MODE_DIFFERS) ||
 					   INFO_TEST(flags, FILE_INFO_GID_DIFFERS) ||
 					   INFO_TEST(flags, FILE_INFO_UID_DIFFERS)) {
-					if (res == 0) res = preceding->install_info(context->depot->m_prefix);
+					state = 'M';
+					if (!dryrun && res == 0) {
+						res = preceding->install_info(context->depot->m_prefix);
+					}
 				} else {
 					IF_DEBUG("[uninstall]    no changes; leaving in place\n");
 				}
@@ -770,7 +878,9 @@
 			uint32_t info = preceding->info();
 			if (INFO_TEST(info, FILE_INFO_NO_ENTRY | FILE_INFO_ROLLBACK_DATA) &&
 			    !INFO_TEST(info, FILE_INFO_BASE_SYSTEM)) {
-				if (res == 0) res = context->files_to_remove->add(preceding->serial());
+				if (!dryrun && res == 0) {
+					res = context->files_to_remove->add(preceding->serial());
+				}
 			}
 			delete preceding;
 		} else {
@@ -781,7 +891,8 @@
 
 	fprintf(stderr, "%c %s\n", state, file->path());
 
-	if (res != 0) fprintf(stderr, "%s:%d: uninstall failed: %s\n", __FILE__, __LINE__, file->path());
+	if (res != 0) fprintf(stderr, "%s:%d: uninstall failed: %s\n", 
+						  __FILE__, __LINE__, file->path());
 
 	free(actpath);
 	return res;
@@ -789,6 +900,8 @@
 
 int Depot::uninstall(Archive* archive) {
 	extern uint32_t verbosity;
+	extern uint32_t force;
+	extern uint32_t dryrun;
 	int res = 0;
 
 	assert(archive != NULL);
@@ -804,38 +917,67 @@
 		return -1;
 	}
 
+	/** 
+	 * require -f to force uninstalling an archive installed on top of an older
+	 * base system since the rollback archive we'll use will potentially damage
+	 * the base system.
+	 */
+	if (!force && 
+		this->m_build &&
+		archive->build() &&
+		(strcmp(this->m_build, archive->build()) != 0)) {
+		fprintf(stderr, 
+				"-------------------------------------------------------------------------------\n"
+				"The %s root was installed on a different base OS build (%s). The current    \n"
+				"OS build is %s. Uninstalling a root that was installed on a different OS     \n"
+				"build has the potential to damage your OS install due to the fact that the   \n"
+				"rollback data is from the wrong OS version.\n\n"
+				" You must use the force (-f) option to make this potentially unsafe operation  \n"
+				"happen.\n"
+				"-------------------------------------------------------------------------------\n",
+				archive->name(), archive->build(), m_build);
+		return 9999;
+	}
+	
 	res = this->lock(LOCK_EX);
 	if (res != 0) return res;
 
-	// XXX: this may be superfluous
-	// uninstall_file should be smart enough to do a mtime check...
-	if (res == 0) res = this->prune_directories();
+	if (!dryrun) {
+		// XXX: this may be superfluous
+		// uninstall_file should be smart enough to do a mtime check...
+		if (res == 0) res = this->prune_directories();
 
-	// We do this here to get an exclusive lock on the database.
-	if (res == 0) res = this->begin_transaction();
-	if (res == 0) res = m_db->deactivate_archive(serial);
-	if (res == 0) res = this->commit_transaction();
-
+		// We do this here to get an exclusive lock on the database.
+		if (res == 0) res = this->begin_transaction();
+		if (res == 0) res = m_db->deactivate_archive(serial);
+		if (res == 0) res = this->commit_transaction();
+	}
+	
 	InstallContext context(this, archive);
 	if (res == 0) res = this->iterate_files(archive, &Depot::uninstall_file, &context);
 	
-	if (res == 0) res = this->begin_transaction();
-	uint32_t i;
-	for (i = 0; i < context.files_to_remove->count; ++i) {
-		uint64_t serial = context.files_to_remove->values[i];
-		if (res == 0) res = m_db->delete_file(serial);
-	}
-	if (res == 0) res = this->commit_transaction();
+	if (!dryrun) {
+		if (res == 0) res = this->begin_transaction();
+		uint32_t i;
+		for (i = 0; i < context.files_to_remove->count; ++i) {
+			uint64_t serial = context.files_to_remove->values[i];
+			if (res == 0) res = m_db->delete_file(serial);
+		}
+		if (res == 0) res = this->commit_transaction();
 
-	if (res == 0) res = this->begin_transaction();	
-	if (res == 0) res = this->remove(archive);
-	if (res == 0) res = this->commit_transaction();
+		if (res == 0) res = this->begin_transaction();	
+		if (res == 0) res = this->remove(archive);
+		if (res == 0) res = this->commit_transaction();
 
-	// delete all of the expanded archive backing stores to save disk space
-	if (res == 0) res = this->prune_directories();
+		// delete all of the expanded archive backing stores to save disk space
+		if (res == 0) res = this->prune_directories();
 
-	if (res == 0) res = prune_archives();
-
+		if (res == 0) res = this->prune_archive(archive);
+	}
+	
+	if (res == 0) fprintf(stdout, "Uninstalled archive: %llu %s \n",
+						  archive->serial(), archive->name());
+	
 	(void)this->lock(LOCK_SH);
 
 	return res;
@@ -858,14 +1000,22 @@
 	return 0;
 }
 
+void Depot::archive_header() {
+	fprintf(stdout, "%-6s %-36s  %-12s  %-7s  %s\n", 
+			"Serial", "UUID", "Date", "Build", "Name");
+	fprintf(stdout, "====== ====================================  "
+			"============  =======  =================\n");	
+}
+
+
 int Depot::verify(Archive* archive) {
 	int res = 0;
-	fprintf(stdout, "%-6s %-36s  %-23s  %s\n", "Serial", "UUID", "Date Installed", "Name");
-	fprintf(stdout, "====== ====================================  =======================  =================\n");
+	this->archive_header();
 	list_archive(archive, stdout);	
-	fprintf(stdout, "=======================================================================================\n");
+	hr();
 	if (res == 0) res = this->iterate_files(archive, &Depot::verify_file, NULL);
-	fprintf(stdout, "=======================================================================================\n\n");
+	hr();
+	fprintf(stdout, "\n");
 	return res;
 }
 
@@ -879,18 +1029,51 @@
 	struct tm local;
 	time_t seconds = archive->date_installed();
 	localtime_r(&seconds, &local);
-	strftime(date, sizeof(date), "%F %T %Z", &local);
+	strftime(date, sizeof(date), "%b %e %H:%M", &local);
 
-	fprintf((FILE*)context, "%-6llu %-36s  %-23s  %s\n", serial, uuid, date, archive->name());
+	fprintf((FILE*)context, "%-6llu %-36s  %-12s  %-7s  %s\n", 
+			serial, uuid, date, (archive->build()?archive->build():""), archive->name());
 	
 	return 0;
 }
 
 int Depot::list() {
+	return this->list(0, NULL);
+}
+
+int Depot::list(int count, char** args) {
 	int res = 0;
-	fprintf(stdout, "%-6s %-36s  %-23s  %s\n", "Serial", "UUID", "Date Installed", "Name");
-	fprintf(stdout, "====== ====================================  =======================  =================\n");
-	if (res == 0) res = this->iterate_archives(&Depot::list_archive, stdout);
+
+	this->archive_header();
+	
+	// handle the default case of "all"
+	if (count == 0) return this->iterate_archives(&Depot::list_archive, stdout);
+
+	Archive** list;
+	Archive* archive;
+	uint32_t archcnt;
+	for (int i = 0; res == 0 && i < count; i++) {
+		list = NULL;
+		archive = NULL;
+		archcnt = 0;
+		// check for special keywords
+		if (strncasecmp(args[i], "all", 3) == 0) {
+			list = this->get_all_archives(&archcnt);
+		} else if (strncasecmp(args[i], "superseded", 10) == 0) {
+			list = this->get_superseded_archives(&archcnt);
+		} 
+		if (archcnt) {
+			// loop over special keyword results
+			for (uint32_t j = 0; res == 0 && j < archcnt; j++) {
+				res = this->list_archive(list[j], stdout);
+			}
+		} else {
+			// arg is a single-archive specifier
+			archive = this->get_archive(args[i]);
+			if (archive) res = this->list_archive(archive, stdout);
+		}
+	}
+	
 	return res;
 }
 
@@ -903,12 +1086,12 @@
 
 int Depot::files(Archive* archive) {
 	int res = 0;
-	fprintf(stdout, "%-6s %-36s  %-23s  %s\n", "Serial", "UUID", "Date Installed", "Name");
-	fprintf(stdout, "====== ====================================  =======================  =================\n");
+	this->archive_header();
 	list_archive(archive, stdout);
-	fprintf(stdout, "=======================================================================================\n");
+	hr();
 	if (res == 0) res = this->iterate_files(archive, &Depot::print_file, stdout);
-	fprintf(stdout, "=======================================================================================\n\n");
+	hr();
+	fprintf(stdout, "\n");
 	return res;
 }
 
@@ -916,9 +1099,10 @@
 	Depot* depot = (Depot*)context;
 	int res = 0;
 	list_archive(archive, stdout);
-	fprintf(stdout, "=======================================================================================\n");
+	hr();
 	if (res == 0) res = depot->iterate_files(archive, &Depot::print_file, stdout);
-	fprintf(stdout, "=======================================================================================\n\n");
+	hr();
+	fprintf(stdout, "\n");
 	return res;
 }
 
@@ -926,8 +1110,7 @@
 	extern uint32_t verbosity;
 	verbosity = 0xFFFFFFFF; // dump is intrinsically a debug command
 	int res = 0;
-	fprintf(stdout, "%-6s %-36s  %-23s  %s\n", "Serial", "UUID", "Date Installed", "Name");
-	fprintf(stdout, "====== ====================================  =======================  =================\n");
+	this->archive_header();
 	if (res == 0) res = this->iterate_archives(&Depot::dump_archive, this);
 	return res;
 }
@@ -967,16 +1150,16 @@
 		fprintf(stderr, "The following archive%s in an inconsistent state and must be uninstalled "
 						"before proceeding:\n\n", inactive->count > 1 ? "s are" : " is");
 		uint32_t i;
-		fprintf(stderr, "%-6s %-36s  %-23s  %s\n", "Serial", "UUID", "Date Installed", "Name");
-		fprintf(stderr, "====== ====================================  =======================  =================\n");
+		this->archive_header();
 		for (i = 0; i < inactive->count; ++i) {
 			Archive* archive = this->archive(inactive->values[i]);
 			if (archive) {
-				list_archive(archive, stderr);
+				list_archive(archive, stdout);
 				delete archive;
 			}
 		}
-		fprintf(stderr, "\nWould you like to uninstall %s now? [y/n] ", inactive->count > 1 ? "them" : "it");
+		fprintf(stderr, "\nWould you like to uninstall %s now? [y/n] ", 
+				inactive->count > 1 ? "them" : "it");
 		int c = getchar();
 		fprintf(stderr, "\n");
 		if (c == 'y' || c == 'Y') {
@@ -1009,6 +1192,39 @@
 
 int Depot::is_locked() { return m_is_locked; }
 
+bool Depot::is_superseded(Archive* archive) {
+	int res = DB_OK;
+	uint8_t** filelist;
+	uint8_t* data;
+	uint32_t count;	
+	res = this->m_db->get_files(&filelist, &count, archive);
+	if (FOUND(res)) {
+		for (uint32_t i=0; i < count; i++) {
+			File* file = this->m_db->make_file(filelist[i]);
+			
+			// check for being superseded by a root
+			res = this->m_db->get_next_file(&data, file, FILE_SUPERSEDED);
+			this->m_db->free_file(data);
+			if (FOUND(res)) continue;
+			
+			// check for being superseded by external changes
+			char* actpath;
+			join_path(&actpath, this->prefix(), file->path());
+			File* actual = FileFactory(actpath);
+			free(actpath);
+			uint32_t flags = File::compare(file, actual);
+
+			// not found in database and no changes on disk, 
+			// so file is the current version of actual
+			if (flags == FILE_INFO_IDENTICAL) return false;
+			 
+			// something external changed contents of actual,
+			// so we consider this file superseded (by OS upgrade?)
+		}
+	}
+	return true;			
+}
+
 int Depot::lock(int operation) {
 	int res = 0;
 	if (m_lock_fd == -1) {
@@ -1041,9 +1257,10 @@
 	// Don't insert an archive that is already in the database
 	assert(archive->serial() == 0);
 	archive->m_serial = m_db->insert_archive(archive->uuid(),
-											  archive->info(),
-											  archive->name(),
-											  archive->date_installed());
+											 archive->info(),
+											 archive->name(),
+											 archive->date_installed(),
+											 archive->build());
 	return archive->m_serial == 0;
 }
 
@@ -1133,6 +1350,8 @@
 	
 	if (strncasecmp(arg, "all", 3) == 0) {
 		list = this->get_all_archives(&count);
+	} else if (strncasecmp(arg, "superseded", 10) == 0) {
+		list = this->get_superseded_archives(&count);
 	} else {
 		// make a list of 1 Archive
 		list = (Archive**)malloc(sizeof(Archive*));

Modified: trunk/darwinup/Depot.h
===================================================================
--- trunk/darwinup/Depot.h	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Depot.h	2010-03-12 22:20:40 UTC (rev 773)
@@ -79,12 +79,14 @@
 
 	// returns a list of Archive*. Caller must free the list. 
 	Archive** get_all_archives(uint32_t *count);
+	Archive** get_superseded_archives(uint32_t *count);
 	uint64_t count_archives();
 	
 	int dump();
 	static int dump_archive(Archive* archive, void* context);
 	
 	int list();
+	int list(int count, char** args);
 	static int list_archive(Archive* archive, void* context);
 
 	int install(const char* path);
@@ -112,7 +114,10 @@
 	// test if the depot is currently locked 
 	int is_locked();
 
+	bool is_superseded(Archive* archive);
 
+	void    archive_header();
+	
 protected:
 
 	// Serialize access to the Depot via flock(2).
@@ -139,16 +144,16 @@
 	int     remove(File* file);
 
 	int		analyze_stage(const char* path, Archive* archive, Archive* rollback, int* rollback_files);
+
+	// removes expand and unexpanded files from archives path
 	int		prune_directories();
+	int		prune_archive(Archive* archive);
 	
-	// Removes all archive entries which have no corresponding files entries.
-	int		prune_archives();
-	
 	File*	file_superseded_by(File* file);
 	File*	file_preceded_by(File* file);
 
 	int		check_consistency();
-
+	
 	DarwinupDatabase* m_db;
 	
 	mode_t		m_depot_mode;
@@ -157,6 +162,7 @@
 	char*		m_database_path;
 	char*		m_archives_path;
 	char*		m_downloads_path;
+	char*       m_build;
 	int		    m_lock_fd;
 	int         m_is_locked;
 };

Modified: trunk/darwinup/Table.cpp
===================================================================
--- trunk/darwinup/Table.cpp	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Table.cpp	2010-03-12 22:20:40 UTC (rev 773)
@@ -54,7 +54,8 @@
 	m_delete_sql    = NULL;
 	m_prepared_insert = NULL;
 	m_prepared_update = NULL;
-	m_prepared_delete = NULL;	
+	m_prepared_delete = NULL;
+	m_version       = 0;
 }
 
 Table::~Table() {
@@ -89,12 +90,16 @@
 	return m_name;
 }
 
+uint32_t Table::version() {
+	return m_version;
+}
+
 int Table::set_custom_create(const char* sql) {
 	this->m_custom_create_sql = strdup(sql);
 	return this->m_custom_create_sql == 0;
 }
 
-int Table::add_column(Column* c) {
+int Table::add_column(Column* c, uint32_t schema_version) {
 	// accumulate offsets for columns in m_columns_size
 	c->m_offset = this->m_columns_size;
 	this->m_columns_size += c->size();
@@ -109,7 +114,7 @@
 		m_column_max *= REALLOC_FACTOR;
 	}
 	m_columns[m_column_count++] = c;
-	
+	c->m_version = schema_version;
 	return 0;
 }
 
@@ -189,7 +194,7 @@
 	bool comma = false;  // flag we set to start adding commas
 	
 	// calculate the length of the sql statement
-	size_t size = 27 + 5*m_column_count;
+	size_t size = 28 + 5*m_column_count + strlen(m_name);
 	for (i=0; i<m_column_count; i++) {
 		size += strlen(m_columns[i]->name());
 	}
@@ -241,7 +246,7 @@
 	bool comma = false;  // flag we set to start adding commas
 	
 	// calculate the length of the sql statement
-	size_t size = 27 + 5*m_column_count;
+	size_t size = 28 + 5*m_column_count + strlen(m_name);
 	for (i=0; i<m_column_count; i++) {
 		size += strlen(m_columns[i]->name());
 	}
@@ -273,6 +278,8 @@
 	}
 	strlcat(m_insert_sql, ");", size);
 	
+	IF_SQL("insert sql: %s \n", m_insert_sql);
+	
 	// prepare
 	int res = sqlite3_prepare_v2(db, m_insert_sql, strlen(m_insert_sql), &m_prepared_insert, NULL);
 	if (res != SQLITE_OK) {
@@ -471,6 +478,11 @@
 	return (const char*)m_create_sql;
 }
 
+const char* Table::alter_add_column(uint32_t index) {
+	if (m_columns[index]) return m_columns[index]->alter(m_name);
+	return NULL;
+}
+
 int Table::where_va_columns(uint32_t count, char* query, size_t size, 
 							size_t* used, va_list args) {
 	char tmpstr[256];

Modified: trunk/darwinup/Table.h
===================================================================
--- trunk/darwinup/Table.h	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Table.h	2010-03-12 22:20:40 UTC (rev 773)
@@ -44,12 +44,13 @@
 	virtual ~Table();
 	
 	const char*    name();
+	uint32_t       version();
 
 	// Add custom SQL to table initialization
 	int            set_custom_create(const char* sql);
 	
 	// Column handling
-	int            add_column(Column*);
+	int            add_column(Column*, uint32_t schema_version);
 	Column*        column(uint32_t index);
 	// get the result record offset for column at index
 	int            offset(uint32_t index);
@@ -95,6 +96,7 @@
 protected:
 
 	const char*    create();  
+	const char*    alter_add_column(uint32_t index);
 	
 	int            where_va_columns(uint32_t count, char* query, size_t size, 
 									size_t* used, va_list args);
@@ -106,6 +108,7 @@
 	void           dump_results(FILE* f);	
 	
 	char*          m_name;
+	uint32_t       m_version; // schema version this was added
 
 	char*          m_create_sql;
 	char*          m_custom_create_sql;

Modified: trunk/darwinup/Utils.cpp
===================================================================
--- trunk/darwinup/Utils.cpp	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Utils.cpp	2010-03-12 22:20:40 UTC (rev 773)
@@ -31,16 +31,6 @@
  */
 
 #include "Utils.h"
-#include <assert.h>
-#include <errno.h>
-#include <libgen.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <spawn.h>
-#include <sys/stat.h>
 
 extern char** environ;
 
@@ -147,16 +137,54 @@
 }
 
 int exec_with_args(const char** args) {
+	return exec_with_args_fa(args, NULL);
+}
+
+int exec_with_args_pipe(const char** args, int fd) {
 	int res = 0;
+	posix_spawn_file_actions_t fa;
+
+	res = posix_spawn_file_actions_init(&fa);
+	if (res) {
+		fprintf(stderr, "Error: unable to initialize file actions: %d \n", res);
+		return res;
+	}
+
+	res = posix_spawn_file_actions_adddup2(&fa, fd, 1); // pipe stdout
+	if (res) {
+		fprintf(stderr, "Error: (%d) unable to add dup2 for %d \n", res, fd);
+		return res;
+	}
+
+	res = posix_spawn_file_actions_addclose(&fa, 2); // close stderr
+	if (res) {
+		fprintf(stderr, "Error: (%d) unable to add close for stderr.\n", res);
+		return res;
+	}
+
+	res = posix_spawn_file_actions_addclose(&fa, 0); // close stdin
+	if (res) {
+		fprintf(stderr, "Error: (%d) unable to add close for stdin.\n", res);
+		return res;
+	}
+	
+	res = exec_with_args_fa(args, &fa);
+	posix_spawn_file_actions_destroy(&fa);
+
+	return res;
+}
+
+int exec_with_args_fa(const char** args, posix_spawn_file_actions_t* fa) {
+	int res = 0;
 	pid_t pid;
 	int status;
 	
-	IF_DEBUG("Spawning %s \n", args[0]);
+	IF_DEBUG("Spawn: %s \n", args[0]);
 		
-	res = posix_spawn(&pid, args[0], NULL, NULL, (char**)args, environ);
+	res = posix_spawn(&pid, args[0], fa, NULL, (char**)args, environ);
 	if (res != 0) fprintf(stderr, "Error: Failed to spawn %s: %s (%d)\n", args[0], strerror(res), res);
 	
-	IF_DEBUG("Running %s on pid %d \n", args[0], (int)pid);
+	IF_DEBUG("Running: %s on pid %d \n", args[0], (int)pid);
 
 	do {
 		res = waitpid(pid, &status, 0);
@@ -169,7 +197,7 @@
 		}
 	}
 	
-	IF_DEBUG("Done running %s \n", args[0]);
+	IF_DEBUG("Done: %s \n", args[0]);
 	
 	return res;
 }
@@ -246,6 +274,118 @@
 	return NULL;	
 }
 
+int find_base_system_path(char** output, const char* path) {
+	// find the first /System as we walk up path
+	char system[PATH_MAX];
+	char parent[PATH_MAX];
+	strlcpy(parent, path, PATH_MAX);
+	int res = -1;
+	struct stat sb;
+	while (res) {
+		// walk up path
+		snprintf(system, PATH_MAX, "%s%sSystem", 
+				 parent, (parent[1] == '\0' ? "" : "/"));
+		res = stat(system, &sb);
+		if (parent[1] == '\0') {
+			// we hit the top of the filesystem
+			break;
+		}
+		if (res) snprintf(parent, PATH_MAX, "%s", dirname(parent));
+	}
+	if (res) {
+		fprintf(stderr, "Error: (%d) unable to find base system path.\n", res);
+		return res;
+	}
+	
+	asprintf(output, "%s", parent);
+	return 0;
+}
+
+int update_dyld_shared_cache(const char* path) {
+	extern uint32_t verbosity;
+	int res;
+	char* base;
+	res = find_base_system_path(&base, path);
+	if (res) return res;
+	
+	if (verbosity) {
+		fprintf(stdout, "Updating dyld shared cache for %s ... ", base);
+		fflush(stdout);
+	}
+	
+	// exec the tool from our target system
+	char* toolpath;
+	join_path(&toolpath, base, "/usr/bin/update_dyld_shared_cache");
+
+	const char* args[] = {
+		toolpath,
+		"-root", base,
+		NULL
+	};
+	res = exec_with_args(args);
+
+	if (verbosity) fprintf(stdout, "Done updating dyld shared cache\n");
+	
+	free(toolpath);
+	free(base);
+	return res;
+}
+
+int build_number_for_path(char** build, const char* path) {
+	int res = 0;
+	char system[PATH_MAX];
+	char* base;
+	
+	*build = (char*)calloc(1, 16);
+	
+	// find the version plist for our target path
+	find_base_system_path(&base, path);
+	if (!base) return 1;	
+	snprintf(system, PATH_MAX, "%s/System/Library/CoreServices/SystemVersion.plist", base);
+	free(base);
+
+	struct stat sb;
+	res = stat(system, &sb);
+	if (res) {
+		snprintf(*build, 16, " ");
+		return 1;
+	}
+
+	// read version plist to get build number
+	const char* args[] = {
+		"/usr/libexec/PlistBuddy",
+		"-c", "Print ProductBuildVersion",
+		system,
+		NULL
+	};
+	int pfd[2];
+	res = pipe(pfd);
+	if (res) {
+		fprintf(stderr, "Error: (%d) failed to create pipe.\n", res);
+		return res;
+	}
+	exec_with_args_pipe(args, pfd[1]);
+	
+	// read from the pipe
+	close(pfd[1]);
+	res = 1;
+	while (res > 0 && res < 15) {
+		res = read(pfd[0], *build, 15);
+		// strip newline
+		if (res > 1 && (*build)[res-1] == '\n') (*build)[res-1] = '\0';
+	}
+	close(pfd[0]);
+
+	if (res == 0) return res; // success
+	
+	if (res == -1) {
+		fprintf(stderr, "Error: failed to read build from plist.\n");
+		return res;
+	}
+
+	return -1;
+}
+
 void __data_hex(FILE* f, uint8_t* data, uint32_t size) {
 	if (!size) return;
 	for (uint32_t i=0; i < size; i++) {
@@ -263,3 +403,8 @@
 	fprintf(f, "\n");
 }
 
+void hr() {
+	fprintf(stdout, "=============================================="
+			"=======================================\n");	
+}
+

Modified: trunk/darwinup/Utils.h
===================================================================
--- trunk/darwinup/Utils.h	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/Utils.h	2010-03-12 22:20:40 UTC (rev 773)
@@ -36,14 +36,25 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <fts.h>
-
 #include <stdarg.h>
 #include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <spawn.h>
+#include <sys/stat.h>
 
-const uint32_t VERBOSE		= 0x1;
+
+const uint32_t VERBOSE		    = 0x1;
 const uint32_t VERBOSE_DEBUG	= 0x2;
+const uint32_t VERBOSE_SQL      = 0x4;
 
 #define IF_DEBUG(...) do { extern uint32_t verbosity; if (verbosity & VERBOSE_DEBUG) fprintf(stderr, "DEBUG: " __VA_ARGS__); } while (0)
+#define IF_SQL(...) do { extern uint32_t verbosity; if (verbosity & VERBOSE_SQL) fprintf(stderr, "DEBUG: " __VA_ARGS__); } while (0)
 
 int fts_compare(const FTSENT **a, const FTSENT **b);
 int ftsent_filename(FTSENT* ent, char* filename, size_t bufsiz);
@@ -54,7 +65,10 @@
 int is_url_path(const char* path);
 int is_userhost_path(const char* path);
 int has_suffix(const char* str, const char* sfx);
+
 int exec_with_args(const char** args);
+int exec_with_args_pipe(const char** args, int fd);
+int exec_with_args_fa(const char** args, posix_spawn_file_actions_t* fa);
 
 int join_path(char** out, const char* p1, const char* p2);
 int compact_slashes(char* orig, int slashes);
@@ -62,8 +76,15 @@
 char* fetch_url(const char* srcpath, const char* dstpath);
 char* fetch_userhost(const char* srcpath, const char* dstpath);
 
+int find_base_system_path(char** output, const char* path);
+int update_dyld_shared_cache(const char* path);
+int build_number_for_path(char** build, const char* path);
+
 void __data_hex(FILE* f, uint8_t* data, uint32_t size);
 
+// print a horizontal line to stdout
+void hr();
+
 inline int INFO_TEST(uint32_t word, uint32_t flag) { return ((word & flag) != 0); }
 inline int INFO_SET(uint32_t word, uint32_t flag) { return (word | flag); }
 inline int INFO_CLR(uint32_t word, uint32_t flag) { return (word & (~flag)); }

Modified: trunk/darwinup/main.cpp
===================================================================
--- trunk/darwinup/main.cpp	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/darwinup/main.cpp	2010-03-12 22:20:40 UTC (rev 773)
@@ -45,17 +45,19 @@
 
 void usage(char* progname) {
 	fprintf(stderr, "usage:    %s [-v] [-p DIR] [command] [args]          \n", progname);
-	fprintf(stderr, "version: 15                                                    \n");
+	fprintf(stderr, "version: 16                                                    \n");
 	fprintf(stderr, "                                                               \n");
 	fprintf(stderr, "options:                                                       \n");
-	fprintf(stderr, "          -f         force operation to succeed at all costs   \n");
-	fprintf(stderr, "          -p DIR     operate on roots under DIR (default: /)   \n");
-	fprintf(stderr, "          -v         verbose (use -vv for extra verbosity)     \n");
+	fprintf(stderr, "          -d        do not update dyld cache                   \n");	
+	fprintf(stderr, "          -f        force operation to succeed at all costs    \n");
+	fprintf(stderr, "          -n        dry run                                    \n");
+	fprintf(stderr, "          -p DIR    operate on roots under DIR (default: /)    \n");
+	fprintf(stderr, "          -v        verbose (use -vv for extra verbosity)      \n");
 	fprintf(stderr, "                                                               \n");
 	fprintf(stderr, "commands:                                                      \n");
 	fprintf(stderr, "          files      <archive>                                 \n");
 	fprintf(stderr, "          install    <path>                                    \n");
-	fprintf(stderr, "          list                                                 \n");
+	fprintf(stderr, "          list       [archive]                                 \n");
 	fprintf(stderr, "          uninstall  <archive>                                 \n");
 	fprintf(stderr, "          upgrade    <path>                                    \n");
 	fprintf(stderr, "          verify     <archive>                                 \n");
@@ -71,13 +73,15 @@
 	fprintf(stderr, "          tar, tar.gz, tar.bz2                                 \n");
 	fprintf(stderr, "          xar, zip                                             \n");
 	fprintf(stderr, "                                                               \n");
-	fprintf(stderr, "<archive> is one of:                                           \n");
-	fprintf(stderr, "          <serial>   the Serial number                         \n");
-	fprintf(stderr, "          <uuid>     the UUID                                  \n");
-	fprintf(stderr, "          <name>     the last root installed with that name    \n");
-	fprintf(stderr, "          newest     the newest (last) root installed          \n");
-	fprintf(stderr, "          oldest     the oldest root installed                 \n");
-	fprintf(stderr, "          all        all installed roots                       \n");
+	fprintf(stderr, "archive is one of:                                             \n");
+	fprintf(stderr, "          <serial>     the Serial number                       \n");
+	fprintf(stderr, "          <uuid>       the UUID                                \n");
+	fprintf(stderr, "          <name>       the last root installed with that name  \n");
+	fprintf(stderr, "          newest       the newest (last) root installed        \n");
+	fprintf(stderr, "          oldest       the oldest root installed               \n");
+	fprintf(stderr, "          superseded   all roots that have been fully replaced \n");
+	fprintf(stderr, "                        by newer roots                         \n");
+	fprintf(stderr, "          all          all installed roots                     \n");
 	fprintf(stderr, "                                                               \n");
 	exit(1);
 }
@@ -85,20 +89,28 @@
 // our globals
 uint32_t verbosity;
 uint32_t force;
+uint32_t dryrun;
 
 
 int main(int argc, char* argv[]) {
 	char* progname = strdup(basename(argv[0]));      
-
 	char* path = NULL;
+	bool update_dyld = true;
 
 	int ch;
-	while ((ch = getopt(argc, argv, "fp:vh")) != -1) {
+	while ((ch = getopt(argc, argv, "dfnp:vh")) != -1) {
 		switch (ch) {
+		case 'd':
+				update_dyld = false;
+				break;
 		case 'f':
 				IF_DEBUG("forcing operations\n");
 				force = 1;
 				break;
+		case 'n':
+				IF_DEBUG("dry run\n");
+				dryrun = 1;
+				break;
 		case 'p':
 				if (optarg[0] != '/') {
 					fprintf(stderr, "Error: -p option must be an absolute path\n");
@@ -129,54 +141,67 @@
 	if (!path) {
 		asprintf(&path, "/");
 	}
-		
+
 	Depot* depot = new Depot(path);
 		
-	// commands with no arguments
-	if (argc == 1) {
-		if (strcmp(argv[0], "list") == 0) {
-			res = depot->initialize(false);
-			if (res == -2) {
-				fprintf(stdout, "Nothing has been installed yet.\n");
-				exit(0);
-			}
-			if (res == 0) depot->list();
-		} else if (strcmp(argv[0], "dump") == 0) {
+	// list handles args optional and in special ways
+	if (strcmp(argv[0], "list") == 0) {
+		res = depot->initialize(false);
+		if (res == -2) {
+			// we are not asking to write, 
+			// but no depot exists yet either,
+			// so print an empty list
+			depot->archive_header();
+			exit(0);
+		}
+		if (res == -3) {
+			// permission denied when trying to read
+			// the depot
+			fprintf(stderr, "Permission denied when trying to read the database.\n");
+			exit(6);
+		}
+		if (res == 0) depot->list(argc-1, (char**)(argv+1));
+	} else if (argc == 1) {
+		// other commands which take no arguments
+		if (strcmp(argv[0], "dump") == 0) {
 			if (depot->initialize(false)) exit(11);
 			depot->dump();
 		} else {
 			usage(progname);
 		}
-	}
-
-	// loop over arguments
-	for (int i = 1; i < argc; i++) {
-		if (strcmp(argv[0], "install") == 0) {
-			if (i==1 && depot->initialize(true)) exit(13);
-			res = depot->install(argv[i]);
-		} else if (strcmp(argv[0], "upgrade") == 0) {
-			if (i==1 && depot->initialize(true)) exit(14);
-			// find most recent matching archive by name
-			Archive* old = depot->get_archive(basename(argv[i]));
-			if (!old) {
-				fprintf(stderr, "Error: unable to find a matching root to upgrade.\n");
-				res = 5;
+	} else {
+		// loop over arguments
+		for (int i = 1; i < argc; i++) {
+			if (strcmp(argv[0], "install") == 0) {
+				if (i==1 && depot->initialize(true)) exit(13);
+				res = depot->install(argv[i]);
+				if (update_dyld && res == 0) res = update_dyld_shared_cache(path);
+			} else if (strcmp(argv[0], "upgrade") == 0) {
+				if (i==1 && depot->initialize(true)) exit(14);
+				// find most recent matching archive by name
+				Archive* old = depot->get_archive(basename(argv[i]));
+				if (!old) {
+					fprintf(stderr, "Error: unable to find a matching root to upgrade.\n");
+					res = 5;
+				}
+				// install new archive
+				if (res == 0) res = depot->install(argv[i]);
+				// uninstall old archive
+				if (res == 0) res = depot->uninstall(old);
+				if (update_dyld && res == 0) res = update_dyld_shared_cache(path);
+			} else if (strcmp(argv[0], "files") == 0) {
+				if (i==1 && depot->initialize(false)) exit(12);
+				res = depot->process_archive(argv[0], argv[i]);
+			} else if (strcmp(argv[0], "uninstall") == 0) {
+				if (i==1 && depot->initialize(true)) exit(15);
+				res = depot->process_archive(argv[0], argv[i]);
+				if (update_dyld && res == 0) res = update_dyld_shared_cache(path);
+			} else if (strcmp(argv[0], "verify") == 0) {
+				if (i==1 && depot->initialize(true)) exit(16);
+				res = depot->process_archive(argv[0], argv[i]);
+			} else {
+				usage(progname);
 			}
-			// install new archive
-			if (res == 0) res = depot->install(argv[i]);
-			// uninstall old archive
-			if (res == 0) res = depot->uninstall(old);
-		} else if (strcmp(argv[0], "files") == 0) {
-			if (i==1 && depot->initialize(false)) exit(12);
-			res = depot->process_archive(argv[0], argv[i]);
-		} else if (strcmp(argv[0], "uninstall") == 0) {
-			if (i==1 && depot->initialize(true)) exit(15);
-			res = depot->process_archive(argv[0], argv[i]);
-		} else if (strcmp(argv[0], "verify") == 0) {
-			if (i==1 && depot->initialize(true)) exit(16);
-			res = depot->process_archive(argv[0], argv[i]);
-		} else {
-			usage(progname);
 		}
 	}
 	

Modified: trunk/testing/darwinup/run-tests.sh
===================================================================
--- trunk/testing/darwinup/run-tests.sh	2010-03-12 17:31:03 UTC (rev 772)
+++ trunk/testing/darwinup/run-tests.sh	2010-03-12 22:20:40 UTC (rev 773)
@@ -11,6 +11,7 @@
 DEST=$PREFIX/dest
 DESTTAR=dest.tar.gz
 
+DARWINUP="darwinup -dvvv -p $DEST "
 DIFF="diff -x .DarwinDepot -x broken -qru"
 
 ROOTS="root root2 root3"
@@ -44,22 +45,22 @@
 for R in $ROOTS;
 do
 	echo "INFO: Installing $R ...";
-	darwinup -vv -p $DEST install $PREFIX/$R
-	UUID=$(darwinup -p $DEST list | head -3 | tail -1 | awk '{print $1}')
+	$DARWINUP install $PREFIX/$R
+	UUID=$($DARWINUP list | head -3 | tail -1 | awk '{print $1}')
 	echo "INFO: Uninstalling $R ...";
-	darwinup -vv -p $DEST uninstall $UUID
+	$DARWINUP uninstall $UUID
 	echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 	$DIFF $ORIG $DEST 2>&1
 done
 
 echo "========== TEST: Multiple argument test ==========";
-darwinup -vv -p $DEST install $PREFIX/root{,2,3}
-LINES=$(darwinup -p $DEST list | wc -l)
+$DARWINUP install $PREFIX/root{,2,3}
+LINES=$($DARWINUP list | wc -l)
 if [ $LINES -lt 5 ]; then
 	echo "Failed multiple argument test."
 	exit 1;
 fi
-darwinup -vv -p $DEST uninstall all
+$DARWINUP uninstall all
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
 
@@ -68,13 +69,13 @@
 for R in $ROOTS;
 do
 	echo "INFO: Installing $R ...";
-	darwinup -vv -p $DEST install $PREFIX/$R
+	$DARWINUP install $PREFIX/$R
 done
 for R in $ROOTS;
 do
-	UUID=$(darwinup -p $DEST list | head -3 | tail -1 | awk '{print $1}')
+	UUID=$($DARWINUP list | head -3 | tail -1 | awk '{print $1}')
 	echo "INFO: Uninstalling $UUID ...";
-	darwinup -vv -p $DEST uninstall $UUID
+	$DARWINUP uninstall $UUID
 done	
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
@@ -83,13 +84,13 @@
 for R in $ROOTS;
 do
         echo "INFO: Installing $R ...";
-        darwinup -vv -p $DEST install $PREFIX/$R
+        $DARWINUP install $PREFIX/$R
 done
 for R in $ROOTS;
 do
-        UUID=$(darwinup -p $DEST list | grep $R$ | awk '{print $1}')
+        UUID=$($DARWINUP list | grep $R$ | awk '{print $1}')
         echo "INFO: Uninstalling $UUID ...";
-        darwinup -vv -p $DEST uninstall $UUID
+        $DARWINUP uninstall $UUID
 done
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
@@ -98,13 +99,13 @@
 for R in $ROOTS;
 do
         echo "INFO: Installing $R ...";
-        darwinup -vv -p $DEST install $PREFIX/$R
+        $DARWINUP install $PREFIX/$R
 done
 for R in root2 root3 root;
 do
-        UUID=$(darwinup -p $DEST list | grep $R$ | awk '{print $1}')
+        UUID=$($DARWINUP list | grep $R$ | awk '{print $1}')
         echo "INFO: Uninstalling $UUID ...";
-        darwinup -vv -p $DEST uninstall $UUID
+        $DARWINUP uninstall $UUID
 done
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
@@ -113,45 +114,45 @@
 for R in root3 root2 root;
 do
         echo "INFO: Installing $R ...";
-        darwinup -vv -p $DEST install $PREFIX/$R
+        $DARWINUP install $PREFIX/$R
 done
 for R in root3 root2 root;
 do
-        UUID=$(darwinup -p $DEST list | grep $R$ | awk '{print $1}')
+        UUID=$($DARWINUP list | grep $R$ | awk '{print $1}')
         echo "INFO: Uninstalling $UUID ...";
-        darwinup -vv -p $DEST uninstall $UUID
+        $DARWINUP uninstall $UUID
 done
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
 
 echo "========== TEST: trying large roots ==========";
 echo "INFO: installing 300files";
-darwinup -vv -p $DEST install $PREFIX/300files.tbz2
-darwinup -vv -p $DEST uninstall 300files.tbz2
+$DARWINUP install $PREFIX/300files.tbz2
+$DARWINUP uninstall 300files.tbz2
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
 echo "INFO: installing 300dir";
-darwinup -vv -p $DEST install $PREFIX/300dirs.tbz2
-darwinup -vv -p $DEST uninstall 300dirs.tbz2
+$DARWINUP install $PREFIX/300dirs.tbz2
+$DARWINUP uninstall 300dirs.tbz2
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
 echo "INFO: installing both 300files and 300dirs";
-darwinup -vv -p $DEST install $PREFIX/300dirs.tbz2
-darwinup -vv -p $DEST install $PREFIX/300files.tbz2
-darwinup -vv -p $DEST uninstall 300dirs.tbz2
-darwinup -vv -p $DEST uninstall 300files.tbz2
+$DARWINUP install $PREFIX/300dirs.tbz2
+$DARWINUP install $PREFIX/300files.tbz2
+$DARWINUP uninstall 300dirs.tbz2
+$DARWINUP uninstall 300files.tbz2
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
 
 echo "========== TEST: Try uninstalling with user data in rollback =========="
 echo "INFO: Installing root5 ...";
-darwinup -vv -p $DEST install $PREFIX/root5
-darwinup -vv -p $DEST install $PREFIX/root6
+$DARWINUP install $PREFIX/root5
+$DARWINUP install $PREFIX/root6
 echo "modification" >> $DEST/d/file
-darwinup -vv -p $DEST install $PREFIX/root7
-darwinup -vv -p $DEST uninstall root6
-darwinup -vv -p $DEST uninstall root5
-darwinup -vv -p $DEST uninstall root7
+$DARWINUP install $PREFIX/root7
+$DARWINUP uninstall root6
+$DARWINUP uninstall root5
+$DARWINUP uninstall root7
 stat $DEST/d/file
 rm $DEST/d/file
 rmdir $DEST/d
@@ -159,32 +160,32 @@
 $DIFF $ORIG $DEST 2>&1
 
 echo "========== TEST: Deep rollback while saving user data =========="
-darwinup -vv -p $DEST install $PREFIX/deep-rollback.cpgz
+$DARWINUP install $PREFIX/deep-rollback.cpgz
 echo "modified" >> $DEST/d1/d2/d3/d4/d5/d6/file
-darwinup -vv -p $DEST install $PREFIX/deep-rollback.cpgz
-darwinup -vv -p $DEST uninstall newest
-darwinup -vv -p $DEST uninstall newest
+$DARWINUP install $PREFIX/deep-rollback.cpgz
+$DARWINUP uninstall newest
+$DARWINUP uninstall newest
 stat $DEST/d1/d2/d3/d4/d5/d6/file
 rm $DEST/d1/d2/d3/d4/d5/d6/file
 rm -rf $DEST/d1
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
 
-darwinup -vv -p $DEST install $PREFIX/deep-rollback.cpgz
-darwinup -vv -p $DEST install $PREFIX/deep-rollback-2.xar
-darwinup -vv -p $DEST uninstall all
+$DARWINUP install $PREFIX/deep-rollback.cpgz
+$DARWINUP install $PREFIX/deep-rollback-2.xar
+$DARWINUP uninstall all
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
 
 
 echo "========== TEST: Testing broken symlink handling =========="
-darwinup -vv -p $DEST install $PREFIX/symlinks
-darwinup -vv -p $DEST uninstall symlinks
+$DARWINUP install $PREFIX/symlinks
+$DARWINUP uninstall symlinks
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
-darwinup -vv -p $DEST install $PREFIX/symlink_update
+$DARWINUP install $PREFIX/symlink_update
 stat -L $DEST/broken
-darwinup -vv -p $DEST uninstall newest
+$DARWINUP uninstall newest
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
 
@@ -194,7 +195,7 @@
 #
 set +e
 echo "========== TEST: Trying a root that will fail due to object change =========="
-darwinup -vv -p $DEST install $PREFIX/root4
+$DARWINUP install $PREFIX/root4
 if [ $? -ne 1 ]; then exit 1; fi
 echo "DIFF: diffing original test files to dest (should be no diffs) ..."
 $DIFF $ORIG $DEST 2>&1
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/darwinbuild-changes/attachments/20100312/bdf24aed/attachment-0001.html>


More information about the darwinbuild-changes mailing list