Billy Barrow vor 6 Monaten
Ursprung
Commit
f2d5dbc4d9
6 geänderte Dateien mit 135 neuen und 22 gelöschten Zeilen
  1. 1 0
      README.md
  2. 13 1
      src/lib/Manifest.vala
  3. 20 2
      src/lib/Paths.vala
  4. 10 19
      src/lib/ResourceRef.vala
  5. 90 0
      src/lib/Tag/Tag.vala
  6. 1 0
      src/lib/meson.build

+ 1 - 0
README.md

@@ -70,6 +70,7 @@
 - "vapi": Vala API: file name in /
 - "gir": GObject introspection: file name locatable in /usr/share/gir
 - "typelib": GObject typelib: file name locatable in /usr/lib64/girepository-1.0, /usr/lib/girepository-1.0, /lib64/girepository-1.0, or /lib/girepository-1.0
+- "tag": A USM system tag, dot seperated with ".tag" appended, i.e. "hello.world" would be locatable in /usr/share/usm-tags/hello/world.tag
 
 ## Source Repositories (Repo.usmr)
 

+ 13 - 1
src/lib/Manifest.vala

@@ -162,9 +162,21 @@ namespace Usm {
             // Install each resource speficied by the manifest
             var resource_count = provides.count();
             var resources_installed = 0;
-            foreach (var resource in provides) {
+
+            // Install from shortest path to longest path, to ensure directories are created before children
+            var install_order = provides.sort((a, b) => paths.get_suggested_path(a.key).length - paths.get_suggested_path(b.key).length);
+            foreach (var resource in install_order) {
                 callback(resource.key, resources_installed, resource_count, 0.0f);
                 var path = paths.get_suggested_path(resource.key);
+                
+                if(resource.key.resource_type == ResourceType.TAG) {
+                    // Ensure parent directories are created first
+                    var parent_dir = File.new_for_path(Path.get_basename(path));
+                    if(!parent_dir.query_exists()) {
+                        parent_dir.make_directory_with_parents();
+                    }
+                }
+
                 if(resource.value.file_type == Usm.ManifestFileType.REGULAR) {
                     var src = File.new_build_filename(build_path, resource.value.path);
                     var dest = File.new_for_path(path);

+ 20 - 2
src/lib/Paths.vala

@@ -17,6 +17,7 @@ namespace Usm {
         public string sbin { get; set; }
         public string shared_state { get; set; }
         public string sys_config { get; set; }
+        public string tags { get; set; }
 
         public string usm_config_dir { get; set; }
 
@@ -35,6 +36,7 @@ namespace Usm {
             Environment.set_variable("SBINDIR", sbin, true);
             Environment.set_variable("SHAREDSTATEDIR", shared_state, true);
             Environment.set_variable("SYSCONFIGDIR", sys_config, true);
+            Environment.set_variable("TAGSDIR", tags, true);
         }
 
         public string get_suggested_path(ResourceRef resource) {
@@ -46,9 +48,9 @@ namespace Usm {
                 case ResourceType.OPTIONAL:
                     return Path.build_filename(destination, "opt", resource.resource);
                 case Usm.ResourceType.RESOURCE:
-                    return Path.build_filename(destination, prefix, "share", resource.resource);
+                    return Path.build_filename(destination, prefix, data, resource.resource);
                 case ResourceType.CONFIGURATION:
-                    return Path.build_filename(destination, "etc", resource.resource);
+                    return Path.build_filename(destination, sys_config, resource.resource);
                 case Usm.ResourceType.BINARY:
                     return Path.build_filename(destination, prefix, bin, resource.resource);
                 case Usm.ResourceType.SUPER_BINARY:
@@ -77,11 +79,25 @@ namespace Usm {
                     return Path.build_filename(destination, prefix, "share", "gir-1.0", resource.resource);
                 case Usm.ResourceType.TYPELIB:
                     return Path.build_filename(destination, prefix, lib, "girepository-1.0", resource.resource);
+                case Usm.ResourceType.TAG:
+                    return get_tag_file_path(resource.resource);
                 default:
                     assert_not_reached();
             }
         }
 
+        public string get_tag_file_path(string tag) {
+            var tag_path = tag.replace(".", "/");
+            tag_path += ".tag";
+
+            return Path.build_filename(destination, prefix, tags, tag_path);
+        }
+
+        public string get_tag_dir_path(string tag) {
+            var tag_path = tag.replace(".", "/");
+            return Path.build_filename(destination, prefix, tags, tag_path);
+        }
+
         public Paths.defaults() {
             destination = "/";
             prefix = "/usr";
@@ -97,6 +113,7 @@ namespace Usm {
             sbin = "sbin";
             shared_state = "com";
             sys_config = "etc";
+            tags = "share/usm-tags";
 
             usm_config_dir = "/etc/usm";
         }
@@ -118,6 +135,7 @@ namespace Usm {
             sbin = Environment.get_variable("USM_SBINDIR") ?? defaults.sbin;
             shared_state = Environment.get_variable("USM_SHAREDSTATEDIR") ?? defaults.shared_state;
             sys_config = Environment.get_variable("USM_SYSCONFIGDIR") ?? defaults.sys_config;
+            tags = Environment.get_variable("USM_TAGSDIR") ?? defaults.tags;
 
             usm_config_dir = Environment.get_variable("USM_CONFIGDIR") ?? defaults.usm_config_dir;
         }

+ 10 - 19
src/lib/ResourceRef.vala

@@ -3,7 +3,6 @@ using Invercargill.Convert;
 namespace Usm {
 
     public enum ResourceType {
-        PACKAGE,
         ROOT_PATH,
         PATH,
         OPTIONAL,
@@ -22,12 +21,11 @@ namespace Usm {
         PKG_CONFIG,
         VALA_API,
         GOBJECT_IR,
-        TYPELIB;
+        TYPELIB,
+        TAG;
 
         public string to_string() {
             switch (this) {
-                case ResourceType.PACKAGE:
-                    return "pkg";
                 case ResourceType.ROOT_PATH:
                     return "rootpath";
                 case ResourceType.PATH:
@@ -66,6 +64,8 @@ namespace Usm {
                     return "gir";
                 case ResourceType.TYPELIB:
                     return "typelib";
+                case ResourceType.TAG:
+                    return "tag";
                 default:
                     assert_not_reached();
             }
@@ -73,8 +73,6 @@ namespace Usm {
 
         public static ResourceType from_string(string str) throws ManifestError {
             switch (str) {
-                case "pkg":
-                    return ResourceType.PACKAGE;
                 case "rootpath":
                     return ResourceType.ROOT_PATH;
                 case "path":
@@ -113,6 +111,8 @@ namespace Usm {
                     return ResourceType.GOBJECT_IR;
                 case "typelib":
                     return ResourceType.TYPELIB;
+                case "tag":
+                    return ResourceType.TAG;
                 default:
                     throw new ManifestError.INVALID_RESOURCE_TYPE(@"Unknown resource type \"$str\".");
             }
@@ -145,8 +145,6 @@ namespace Usm {
         
         public bool is_satisfied() {
             switch (resource_type) {
-                case ResourceType.PACKAGE:
-                    return check_pkg();
                 case ResourceType.ROOT_PATH:
                     return check_rootpath();
                 case ResourceType.PATH:
@@ -185,22 +183,15 @@ namespace Usm {
                     return check_gir();
                 case ResourceType.TYPELIB:
                     return check_typelib();
+                case ResourceType.TAG:
+                    return check_tag();
                 default:
                     assert_not_reached();
             }
         }
 
-        private bool check_pkg() {
-            var state = new SystemState(new Paths.usm_environ());
-            try {
-                return state.get_installed_packages()
-                    .try_select<Manifest>(p => p.get_manifest())
-                    .results.any(m => m.name == resource);
-            }
-            catch(Error e) {
-                warning(@"Error checking for resource $this: $(e.message)");
-            }
-            return false;
+        private bool check_tag() {
+            return Tag.read(resource) != null;
         }
 
         private bool check_lib() {

+ 90 - 0
src/lib/Tag/Tag.vala

@@ -0,0 +1,90 @@
+using Invercargill;
+using Invercargill.Convert;
+
+namespace Usm {
+
+    public class Tag {
+        private static string[] INVALID_SEQUENCES = {".tag", " ", "/", "*", "\"", "\\", ">", "<", ":", "|", "?"};
+
+        public string name { get; private set; }
+
+        public string version { get; set; }
+        public string owner { get; set; }
+        public Properties data { get; set; }
+
+        public Tag(string name) throws TagError {
+            if(name.has_suffix(".tag")) {
+                throw new TagError.INVALID_NAME("Tags may not be called \"tag\"");
+            }
+            if(name.down() != name) {
+                throw new TagError.INVALID_NAME("Tags may only contain lowercase characters");
+            }
+            if(ate(INVALID_SEQUENCES).any(seq => name.contains(seq))) {
+                var sequences = ate(INVALID_SEQUENCES).to_string(s => @"\"$s\"", ", ");
+                throw new TagError.INVALID_NAME(@"Tags may not contain any of the following characters or sequences: $sequences");
+            }
+            this.name = name;
+        }
+
+        public static Tag? read(string name) {
+            var paths = new Paths.usm_environ();
+            var path = paths.get_tag_file_path(name);
+            if(!File.new_for_path(path).query_exists()) {
+                return null;
+            }
+
+            try {
+                var element = new InvercargillJson.JsonElement.from_file(path);
+                var tag = Tag.get_mapper().materialise(element.as<Invercargill.Properties>());
+                tag.name = name;
+                return tag;
+            }
+            catch(Error e) {
+                critical(@"Failed to read tag $(name) ($(path)): $(e.message)");
+                return null;
+            }
+        }
+        
+        public Tag? get_parent() {
+            var parts = ate(name.split("."));
+            if(parts.count() < 2) {
+                return null;
+            }
+            return read(parts.take(parts.count() - 1).to_string(s => s, "."));
+        }
+
+        public Enumerable<Tag> get_children() {
+            var paths = new Paths.usm_environ();
+            var path = paths.get_tag_dir_path(name);
+            if(!File.new_for_path(path).query_exists()) {
+                return Invercargill.empty<Tag>();
+            }
+
+            try {
+                return directory(path)
+                    .where(f => f.has_suffix(".tag"))
+                    .select<Tag>(f => read(f.substring(0, f.length - 4)))
+                    .where(t => t != null);
+            }
+            catch (Error e) {
+                critical(@"Could not read children of tag $name: failed to read directory $(path): $(e.message)");
+                return Invercargill.empty<Tag>();
+            }
+        }
+
+
+        private Tag.empty() {}
+        private static PropertyMapper<Tag> get_mapper() {
+            return PropertyMapper.build_for<Tag>(cfg => {
+                cfg.map<string>("owner", o => o.owner, (o, v) => o.owner = v, true);
+                cfg.map<string>("version", o => o.version, (o, v) => o.version = v, true);
+                cfg.map<Properties>("data", o => o.data, (o, v) => o.data = v, false);
+                cfg.set_constructor(() => new Tag.empty());
+            });
+        }
+    }
+
+    public errordomain TagError {
+        INVALID_NAME;
+    }
+}

+ 1 - 0
src/lib/meson.build

@@ -21,6 +21,7 @@ sources += files('Repository/GioRepositoryClient.vala')
 sources += files('State/State.vala')
 sources += files('State/CachedPackage.vala')
 sources += files('State/OriginInformation.vala')
+sources += files('Tag/Tag.vala')
 
 
 dependencies = [