Allow .Deleted to work on directories. Fixes #2641. Patch by leper.
This was SVN commit r18916.
This commit is contained in:
parent
01b667ab86
commit
12ad190a51
@ -22,13 +22,13 @@
|
||||
# Choices are "x86_64" or "i386" (ppc and ppc64 not supported)
|
||||
export ARCH=${ARCH:="x86_64"}
|
||||
|
||||
OSX_VERSION=`sw_vers -productVersion | grep -Eo "^\d+.\d+"`
|
||||
OSX_VERSION='10.12'
|
||||
# Set SDK and mimimum required OS X version
|
||||
export SYSROOT=${SYSROOT:="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX$OSX_VERSION.sdk"}
|
||||
export MIN_OSX_VERSION=${MIN_OSX_VERSION:="10.7"}
|
||||
|
||||
# 0 A.D. release version, e.g. Alpha 21 is 0.0.21
|
||||
BUNDLE_VERSION=${BUNDLE_VERSION:="0.0.X"}
|
||||
BUNDLE_VERSION=${BUNDLE_VERSION:="0.0.21"}
|
||||
|
||||
# Define compiler as "clang", this is all Mavericks supports.
|
||||
# gcc symlinks may still exist, but they are simply clang with
|
||||
@ -104,10 +104,17 @@ BUNDLE_SHAREDSUPPORT=$BUNDLE_CONTENTS/SharedSupport
|
||||
# TODO: Do we really want to regenerate everything? (consider if one task fails)
|
||||
|
||||
# Build libraries against SDK
|
||||
echo "\nBuilding libraries\n"
|
||||
pushd ../../libraries/osx > /dev/null
|
||||
./build-osx-libs.sh $JOBS --force-rebuild >> $build_log 2>&1 || die "Libraries build script failed"
|
||||
popd > /dev/null
|
||||
echo "\nSymlinking libraries\n"
|
||||
cd ../../
|
||||
ln -s /Users/Lancelot/Desktop/git-0AD/libraries
|
||||
cd binaries/data/mods/public/
|
||||
ln -s /Users/Lancelot/Desktop/git-0AD/binaries/data/mods/public/art
|
||||
ln -s /Users/Lancelot/Desktop/git-0AD/binaries/data/mods/public/audio
|
||||
cd ../../../../build/workspaces
|
||||
#echo "\nBuilding libraries\n"
|
||||
#pushd ../../libraries/osx > /dev/null
|
||||
#./build-osx-libs.sh $JOBS --force-rebuild >> $build_log 2>&1 || die "Libraries build script failed"
|
||||
#popd > /dev/null
|
||||
|
||||
# Clean and update workspaces
|
||||
echo "\nGenerating workspaces\n"
|
||||
@ -117,6 +124,8 @@ echo "\nGenerating workspaces\n"
|
||||
|
||||
pushd gcc > /dev/null
|
||||
echo "\nBuilding game\n"
|
||||
patch -p0 -i "/Users/Lancelot/diff.patch"
|
||||
patch -p0 -i "/Users/Lancelot/diff2.patch"
|
||||
(make clean && CC="$CC -arch $ARCH" CXX="$CXX -arch $ARCH" make ${JOBS}) >> $build_log 2>&1 || die "Game build failed!"
|
||||
popd > /dev/null
|
||||
|
||||
@ -211,7 +220,7 @@ PlistBuddy -c "Add :CFBundleDevelopmentRegion string English" ${INFO_PLIST}
|
||||
PlistBuddy -c "Add :CFBundleInfoDictionaryVersion string 6.0" ${INFO_PLIST}
|
||||
PlistBuddy -c "Add :CFBundleIconFile string 0ad" ${INFO_PLIST}
|
||||
PlistBuddy -c "Add :LSMinimumSystemVersion string ${BUNDLE_MIN_OSX_VERSION}" ${INFO_PLIST}
|
||||
PlistBuddy -c "Add :NSHumanReadableCopyright string Copyright © 2015 Wildfire Games" ${INFO_PLIST}
|
||||
PlistBuddy -c "Add :NSHumanReadableCopyright string Copyright © 2016 Wildfire Games" ${INFO_PLIST}
|
||||
|
||||
# TODO: Automatically create compressed DMG with hdiutil?
|
||||
# (this is a bit complicated so I do it manually for now)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2013 Wildfire Games
|
||||
/* Copyright (c) 2016 Wildfire Games
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@ -22,8 +22,10 @@
|
||||
|
||||
#include "lib/self_test.h"
|
||||
|
||||
#include "lib/file/vfs/vfs_tree.h"
|
||||
#include "lib/file/common/file_loader.h"
|
||||
#include "lib/file/vfs/vfs.h"
|
||||
#include "lib/file/vfs/vfs_lookup.h"
|
||||
#include "lib/file/vfs/vfs_tree.h"
|
||||
|
||||
class MockLoader : public IFileLoader
|
||||
{
|
||||
@ -43,6 +45,99 @@ public:
|
||||
|
||||
class TestVfsTree : public CxxTest::TestSuite
|
||||
{
|
||||
/**
|
||||
* We create a few different "mods" here to test proper .DELETED
|
||||
* behavior.
|
||||
*
|
||||
* To check which file is used we use the priority.
|
||||
*
|
||||
* 1
|
||||
* +--a
|
||||
* +--b/
|
||||
* | +--a
|
||||
* | \--b/
|
||||
* | \--a
|
||||
* \--c/
|
||||
* +--a
|
||||
* \--b
|
||||
*
|
||||
* 2
|
||||
* +--a.DELETED
|
||||
* +--b/
|
||||
* | +--a
|
||||
* | \--b.DELETED
|
||||
* +--c.DELETED
|
||||
* \--c/
|
||||
* +--a
|
||||
* \--b
|
||||
*
|
||||
* 3
|
||||
* +--a
|
||||
* \--b/
|
||||
* \--b/
|
||||
* \--a
|
||||
*/
|
||||
void mount_mod(size_t mod, VfsDirectory& dir)
|
||||
{
|
||||
size_t priority = mod;
|
||||
PIFileLoader loader(new MockLoader(1));
|
||||
switch(mod)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
dir.AddFile(VfsFile("a", 0, 0, priority, loader));
|
||||
VfsDirectory* b = dir.AddSubdirectory("b");
|
||||
b->AddFile(VfsFile("a", 0, 0, priority, loader));
|
||||
VfsDirectory* b_b = b->AddSubdirectory("b");
|
||||
b_b->AddFile(VfsFile("a", 0, 0, priority, loader));
|
||||
VfsDirectory* c = dir.AddSubdirectory("c");
|
||||
c->AddFile(VfsFile("a", 0, 0, priority, loader));
|
||||
c->AddFile(VfsFile("b", 0, 0, priority, loader));
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
dir.DeleteSubtree(VfsFile("a.DELETED", 0, 0, priority, loader));
|
||||
VfsDirectory* b = dir.AddSubdirectory("b");
|
||||
b->AddFile(VfsFile("a", 0, 0, priority, loader));
|
||||
b->DeleteSubtree(VfsFile("b.DELETED", 0, 0, priority, loader));
|
||||
dir.DeleteSubtree(VfsFile("c.DELETED", 0, 0, priority, loader));
|
||||
VfsDirectory* c = dir.AddSubdirectory("c");
|
||||
c->AddFile(VfsFile("a", 0, 0, priority, loader));
|
||||
c->AddFile(VfsFile("b", 0, 0, priority, loader));
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
dir.AddFile(VfsFile("a", 0, 0, priority, loader));
|
||||
VfsDirectory* b = dir.AddSubdirectory("b");
|
||||
VfsDirectory* b_b = b->AddSubdirectory("b");
|
||||
b_b->AddFile(VfsFile("a", 0, 0, priority, loader));
|
||||
break;
|
||||
}
|
||||
NODEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
void check_priority(VfsDirectory& root, const VfsPath& path, size_t priority)
|
||||
{
|
||||
VfsDirectory* dir; VfsFile* file;
|
||||
TS_ASSERT_OK(vfs_Lookup(path, &root, dir, &file, VFS_LOOKUP_SKIP_POPULATE));
|
||||
TS_ASSERT_EQUALS(file->Priority(), priority);
|
||||
}
|
||||
|
||||
void file_does_not_exists(VfsDirectory& root, const VfsPath& path)
|
||||
{
|
||||
VfsDirectory* dir; VfsFile* file;
|
||||
TS_ASSERT_EQUALS(vfs_Lookup(path, &root, dir, &file, VFS_LOOKUP_SKIP_POPULATE), ERR::VFS_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
void directory_exists(VfsDirectory& root, const VfsPath& path, Status error = INFO::OK)
|
||||
{
|
||||
VfsDirectory* dir;
|
||||
TS_ASSERT_EQUALS(vfs_Lookup(path, &root, dir, nullptr, VFS_LOOKUP_SKIP_POPULATE), error);
|
||||
}
|
||||
|
||||
public:
|
||||
void test_replacement()
|
||||
{
|
||||
@ -62,4 +157,35 @@ public:
|
||||
TS_ASSERT_EQUALS(dir.AddFile(file2)->MTime(), file2.MTime());
|
||||
TS_ASSERT_EQUALS(dir.AddFile(file1)->MTime(), file2.MTime());
|
||||
}
|
||||
|
||||
void test_deleted()
|
||||
{
|
||||
VfsDirectory dir;
|
||||
|
||||
mount_mod(1, dir);
|
||||
mount_mod(2, dir);
|
||||
file_does_not_exists(dir, "a");
|
||||
check_priority(dir, "b/a", 2);
|
||||
directory_exists(dir, "b/b/", ERR::VFS_DIR_NOT_FOUND);
|
||||
directory_exists(dir, "c/");
|
||||
check_priority(dir, "c/a", 2);
|
||||
check_priority(dir, "c/b", 2);
|
||||
dir.Clear();
|
||||
|
||||
|
||||
mount_mod(1, dir);
|
||||
mount_mod(2, dir);
|
||||
mount_mod(3, dir);
|
||||
check_priority(dir, "a", 3);
|
||||
check_priority(dir, "b/b/a", 3);
|
||||
dir.Clear();
|
||||
|
||||
|
||||
mount_mod(1, dir);
|
||||
mount_mod(3, dir);
|
||||
mount_mod(2, dir);
|
||||
check_priority(dir, "a", 3);
|
||||
check_priority(dir, "b/b/a", 3);
|
||||
dir.Clear();
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2013 Wildfire Games
|
||||
/* Copyright (c) 2016 Wildfire Games
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@ -58,13 +58,13 @@ public:
|
||||
DirectoryNames subdirectoryNames; subdirectoryNames.reserve(50);
|
||||
RETURN_STATUS_IF_ERR(GetDirectoryEntries(m_realDirectory->Path(), &files, &subdirectoryNames));
|
||||
|
||||
// to support .DELETED files inside archives safely, we need to load
|
||||
// archives and loose files in a deterministic order in case they add
|
||||
// and then delete the same file (or vice versa, depending on loading
|
||||
// order). GetDirectoryEntries has undefined order so sort its output
|
||||
std::sort(files.begin(), files.end(), CompareFileInfoByName());
|
||||
std::sort(subdirectoryNames.begin(), subdirectoryNames.end());
|
||||
|
||||
// Since .DELETED files only remove files in lower priority mods
|
||||
// loose files and archive files have no conflicts so we do not need
|
||||
// to sort them.
|
||||
// We add directories after they might have been removed by .DELETED
|
||||
// files (as they did not contain any files at that point). The order
|
||||
// of GetDirectoryEntries is undefined, but that does not really matter (TODO really?)
|
||||
// so we do not need to sort its output.
|
||||
RETURN_STATUS_IF_ERR(AddFiles(files));
|
||||
AddSubdirectories(subdirectoryNames);
|
||||
|
||||
@ -75,14 +75,14 @@ private:
|
||||
void AddFile(const CFileInfo& fileInfo) const
|
||||
{
|
||||
const VfsPath name = fileInfo.Name();
|
||||
const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory);
|
||||
if(name.Extension() == L".DELETED")
|
||||
{
|
||||
m_directory->RemoveFile(name.Basename());
|
||||
m_directory->DeleteSubtree(file);
|
||||
if(!(m_realDirectory->Flags() & VFS_MOUNT_KEEP_DELETED))
|
||||
return;
|
||||
}
|
||||
|
||||
const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory);
|
||||
m_directory->AddFile(file);
|
||||
}
|
||||
|
||||
@ -97,14 +97,14 @@ private:
|
||||
WARN_IF_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags));
|
||||
|
||||
const VfsPath name = fileInfo.Name();
|
||||
const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile);
|
||||
if(name.Extension() == L".DELETED")
|
||||
{
|
||||
directory->RemoveFile(name.Basename());
|
||||
directory->DeleteSubtree(file);
|
||||
if(!(this_->m_realDirectory->Flags() & VFS_MOUNT_KEEP_DELETED))
|
||||
return;
|
||||
}
|
||||
|
||||
const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile);
|
||||
directory->AddFile(file);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2013 Wildfire Games
|
||||
/* Copyright (c) 2016 Wildfire Games
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@ -49,6 +49,13 @@ VfsDirectory::VfsDirectory()
|
||||
{
|
||||
}
|
||||
|
||||
static bool ShouldDelete(const VfsFile& file, const VfsFile& deletedFile)
|
||||
{
|
||||
// We only check priority here, a .DELETED file in a mod should not
|
||||
// delete files in that mod. For the same reason we ignore loose
|
||||
// .DELETED files next to an archive.
|
||||
return file.Priority() < deletedFile.Priority();
|
||||
}
|
||||
|
||||
static bool ShouldReplaceWith(const VfsFile& previousFile, const VfsFile& newFile)
|
||||
{
|
||||
@ -78,6 +85,38 @@ static bool ShouldReplaceWith(const VfsFile& previousFile, const VfsFile& newFil
|
||||
}
|
||||
|
||||
|
||||
void VfsDirectory::DeleteSubtree(const VfsFile& file)
|
||||
{
|
||||
ENSURE(file.Name().Extension() == L".DELETED");
|
||||
|
||||
const VfsPath basename = file.Name().Basename();
|
||||
std::map<VfsPath, VfsFile>::iterator fit = m_files.find(basename);
|
||||
if(fit != m_files.end() && ShouldDelete(fit->second, file))
|
||||
m_files.erase(basename);
|
||||
|
||||
std::map<VfsPath, VfsDirectory>::iterator dit = m_subdirectories.find(basename);
|
||||
if(dit != m_subdirectories.end() && dit->second.DeleteTree(file))
|
||||
m_subdirectories.erase(dit);
|
||||
}
|
||||
|
||||
bool VfsDirectory::DeleteTree(const VfsFile& file)
|
||||
{
|
||||
for(std::map<VfsPath, VfsFile>::iterator it = m_files.begin(); it != m_files.end();)
|
||||
if(ShouldDelete(it->second, file))
|
||||
it = m_files.erase(it);
|
||||
else
|
||||
++it;
|
||||
|
||||
for(std::map<VfsPath, VfsDirectory>::iterator it = m_subdirectories.begin(); it != m_subdirectories.end();)
|
||||
if(it->second.DeleteTree(file))
|
||||
it = m_subdirectories.erase(it);
|
||||
else
|
||||
++it;
|
||||
|
||||
return m_files.empty() && m_subdirectories.empty();
|
||||
}
|
||||
|
||||
|
||||
VfsFile* VfsDirectory::AddFile(const VfsFile& file)
|
||||
{
|
||||
std::pair<VfsPath, VfsFile> value = std::make_pair(file.Name(), file);
|
||||
@ -122,7 +161,6 @@ VfsFile* VfsDirectory::GetFile(const VfsPath& name)
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
|
||||
VfsDirectory* VfsDirectory::GetSubdirectory(const VfsPath& name)
|
||||
{
|
||||
VfsSubdirectories::iterator it = m_subdirectories.find(name.string());
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2010 Wildfire Games
|
||||
/* Copyright (c) 2016 Wildfire Games
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@ -77,12 +77,28 @@ private:
|
||||
|
||||
class VfsDirectory
|
||||
{
|
||||
/**
|
||||
* remove all files with a lower priority than @p file
|
||||
* and do the same for all subdirectories recursively.
|
||||
* @return true if the directory is empty afterwards
|
||||
**/
|
||||
bool DeleteTree(const VfsFile& file);
|
||||
|
||||
public:
|
||||
typedef std::map<VfsPath, VfsFile> VfsFiles;
|
||||
typedef std::map<VfsPath, VfsDirectory> VfsSubdirectories;
|
||||
|
||||
VfsDirectory();
|
||||
|
||||
/**
|
||||
* remove the given file or subdirectory according to the priority of the
|
||||
* passed .DELETED file.
|
||||
* CAUTION: Invalidates all previously returned pointers of the file or
|
||||
* subdirectory (and contents) if those have lower priority
|
||||
* than @p file.
|
||||
**/
|
||||
void DeleteSubtree(const VfsFile& file);
|
||||
|
||||
/**
|
||||
* @return address of existing or newly inserted file.
|
||||
**/
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
/* Copyright (C) 2016 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -77,11 +77,9 @@ void CArchiveBuilder::Build(const OsPath& archive, bool compress)
|
||||
|
||||
CXeromyces xero;
|
||||
|
||||
for (size_t i = 0; i < m_Files.size(); ++i)
|
||||
for (const VfsPath& path : m_Files)
|
||||
{
|
||||
Status ret;
|
||||
|
||||
const VfsPath path = m_Files[i];
|
||||
OsPath realPath;
|
||||
ret = m_VFS->GetRealPath(path, realPath);
|
||||
ENSURE(ret == INFO::OK);
|
||||
|
Loading…
Reference in New Issue
Block a user