LCOV - code coverage report
Current view: top level - modules/script - package.js (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 26.1 % 138 36
Test Date: 2024-04-20 17:42:51 Functions: 13.3 % 15 2
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 47.8 % 46 22

             Branch data     Line data    Source code
       1                 :           1 : // SPDX-FileCopyrightText: 2012 Giovanni Campagna
       2                 :             : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
       3                 :             : 
       4                 :             : /* exported checkSymbol, datadir, init, initFormat, initGettext, initSubmodule,
       5                 :             : libdir, localedir, moduledir, name, pkgdatadir, pkglibdir, prefix, require,
       6                 :             : requireSymbol, run, start, version */
       7                 :             : 
       8                 :             : /**
       9                 :             :  * This module provides a set of convenience APIs for building packaged
      10                 :             :  * applications.
      11                 :             :  */
      12                 :           1 : imports.gi.versions.GIRepository = '2.0';
      13                 :             : 
      14                 :           1 : const GLib = imports.gi.GLib;
      15                 :           1 : const GIRepository = imports.gi.GIRepository;
      16                 :           1 : const Gio = imports.gi.Gio;
      17                 :           1 : const GObject = imports.gi.GObject;
      18                 :           1 : const System = imports.system;
      19                 :             : 
      20                 :           1 : const Gettext = imports.gettext;
      21                 :             : 
      22                 :             : // public
      23                 :             : var name;
      24                 :             : var version;
      25                 :             : var prefix;
      26                 :             : var datadir;
      27                 :             : var libdir;
      28                 :             : var pkgdatadir;
      29                 :             : var pkglibdir;
      30                 :             : var moduledir;
      31                 :             : var localedir;
      32                 :             : 
      33                 :             : // private
      34                 :           1 : let _pkgname;
      35                 :           1 : let _base;
      36                 :           1 : let _submoduledir;
      37                 :             : 
      38                 :           0 : function _findEffectiveEntryPointName() {
      39                 :           0 :     let entryPoint = System.programInvocationName;
      40         [ #  # ]:           0 :     while (GLib.file_test(entryPoint, GLib.FileTest.IS_SYMLINK))
      41                 :           0 :         entryPoint = GLib.file_read_link(entryPoint);
      42                 :             : 
      43                 :           0 :     return GLib.path_get_basename(entryPoint);
      44                 :           0 : }
      45                 :             : 
      46                 :           0 : function _runningFromSource() {
      47                 :           0 :     let binary = Gio.File.new_for_path(System.programInvocationName);
      48                 :           0 :     let sourceBinary = Gio.File.new_for_path(`./src/${name}`);
      49                 :           0 :     return binary.equal(sourceBinary);
      50                 :           0 : }
      51                 :             : 
      52                 :           0 : function _runningFromMesonSource() {
      53         [ #  # ]:           0 :     return GLib.getenv('MESON_BUILD_ROOT') &&
      54                 :           0 :            GLib.getenv('MESON_SOURCE_ROOT');
      55                 :             : }
      56                 :             : 
      57                 :           0 : function _makeNamePath(n) {
      58                 :           0 :     return `/${n.replace(/\./g, '/')}`;
      59                 :             : }
      60                 :             : 
      61                 :             : /**
      62                 :             :  * Initialize directories and global variables. Must be called
      63                 :             :  * before any of other API in Package is used.
      64                 :             :  * `params` must be an object with at least the following keys:
      65                 :             :  *  - name: the package name ($(PACKAGE_NAME) in autotools,
      66                 :             :  *          eg. org.foo.Bar)
      67                 :             :  *  - version: the package version
      68                 :             :  *  - prefix: the installation prefix
      69                 :             :  *
      70                 :             :  * init() will take care to check if the program is running from
      71                 :             :  * the source directory or not, by looking for a 'src' directory.
      72                 :             :  *
      73                 :             :  * At the end, the global variable 'pkg' will contain the
      74                 :             :  * Package module (imports.package). Additionally, the following
      75                 :             :  * module variables will be available:
      76                 :             :  *  - name: the base name of the entry point (eg. org.foo.Bar.App)
      77                 :             :  *  - version: same as in @params
      78                 :             :  *  - prefix: the installation prefix (as passed in @params)
      79                 :             :  *  - datadir, libdir: the final datadir and libdir when installed;
      80                 :             :  *                     usually, these would be prefix + '/share' and
      81                 :             :  *                     and prefix + '/lib' (or '/lib64')
      82                 :             :  *  - pkgdatadir: the directory to look for private data files, such as
      83                 :             :  *                images, stylesheets and UI definitions;
      84                 :             :  *                this will be datadir + name when installed and
      85                 :             :  *                './data' when running from the source tree
      86                 :             :  *  - pkglibdir: the directory to look for private typelibs and C
      87                 :             :  *               libraries;
      88                 :             :  *               this will be libdir + name when installed and
      89                 :             :  *               './lib' when running from the source tree
      90                 :             :  *  - moduledir: the directory to look for JS modules;
      91                 :             :  *               this will be pkglibdir when installed and
      92                 :             :  *               './src' when running from the source tree
      93                 :             :  *  - localedir: the directory containing gettext translation files;
      94                 :             :  *               this will be datadir + '/locale' when installed
      95                 :             :  *               and './po' in the source tree
      96                 :             :  *
      97                 :             :  * All paths are absolute and will not end with '/'.
      98                 :             :  *
      99                 :             :  * As a side effect, init() calls GLib.set_prgname().
     100                 :             :  *
     101                 :             :  * @param {object} params package parameters
     102                 :             :  */
     103                 :           0 : function init(params) {
     104                 :           0 :     globalThis.pkg = imports.package;
     105                 :           0 :     _pkgname = params.name;
     106                 :           0 :     name = _findEffectiveEntryPointName();
     107                 :           0 :     version = params.version;
     108                 :             : 
     109                 :             :     // Must call it first, because it can only be called
     110                 :             :     // once, and other library calls might have it as a
     111                 :             :     // side effect
     112                 :           0 :     GLib.set_prgname(name);
     113                 :             : 
     114                 :           0 :     prefix = params.prefix;
     115                 :           0 :     libdir = params.libdir;
     116                 :           0 :     datadir = GLib.build_filenamev([prefix, 'share']);
     117                 :           0 :     let libpath, girpath;
     118                 :             : 
     119         [ #  # ]:           0 :     if (_runningFromMesonSource()) {
     120                 :           0 :         log('Running from Meson, using local files');
     121                 :           0 :         let bld = GLib.getenv('MESON_BUILD_ROOT');
     122                 :           0 :         let src = GLib.getenv('MESON_SOURCE_ROOT');
     123                 :             : 
     124                 :           0 :         pkglibdir = libpath = girpath = GLib.build_filenamev([bld, 'lib']);
     125                 :           0 :         pkgdatadir = GLib.build_filenamev([bld, 'data']);
     126                 :           0 :         localedir = GLib.build_filenamev([bld, 'po']);
     127                 :           0 :         _submoduledir = GLib.build_filenamev([bld, 'subprojects']);
     128                 :             : 
     129                 :           0 :         GLib.setenv('GSETTINGS_SCHEMA_DIR', pkgdatadir, true);
     130                 :           0 :         try {
     131                 :           0 :             let resource = Gio.Resource.load(GLib.build_filenamev([bld, 'src',
     132                 :           0 :                 `${name}.src.gresource`]));
     133                 :           0 :             resource._register();
     134                 :           0 :             moduledir = `resource://${_makeNamePath(name)}/js`;
     135                 :           0 :         } catch (e) {
     136                 :           0 :             moduledir = GLib.build_filenamev([src, 'src']);
     137                 :             :         }
     138         [ #  # ]:           0 :     } else if (_runningFromSource()) {
     139                 :           0 :         log('Running from source tree, using local files');
     140                 :             :         // Running from source directory
     141                 :           0 :         _base = GLib.get_current_dir();
     142                 :           0 :         _submoduledir = _base;
     143                 :           0 :         pkglibdir = GLib.build_filenamev([_base, 'lib']);
     144                 :           0 :         libpath = GLib.build_filenamev([pkglibdir, '.libs']);
     145                 :           0 :         girpath = pkglibdir;
     146                 :           0 :         pkgdatadir = GLib.build_filenamev([_base, 'data']);
     147                 :           0 :         localedir = GLib.build_filenamev([_base, 'po']);
     148                 :           0 :         moduledir = GLib.build_filenamev([_base, 'src']);
     149                 :             : 
     150                 :           0 :         GLib.setenv('GSETTINGS_SCHEMA_DIR', pkgdatadir, true);
     151                 :             :     } else {
     152                 :           0 :         _base = prefix;
     153                 :           0 :         pkglibdir = GLib.build_filenamev([libdir, _pkgname]);
     154                 :           0 :         libpath = pkglibdir;
     155                 :           0 :         girpath = GLib.build_filenamev([pkglibdir, 'girepository-1.0']);
     156                 :           0 :         pkgdatadir = GLib.build_filenamev([datadir, _pkgname]);
     157                 :           0 :         localedir = GLib.build_filenamev([datadir, 'locale']);
     158                 :             : 
     159                 :           0 :         try {
     160                 :           0 :             let resource = Gio.Resource.load(GLib.build_filenamev([pkgdatadir,
     161                 :           0 :                 `${name}.src.gresource`]));
     162                 :           0 :             resource._register();
     163                 :             : 
     164                 :           0 :             moduledir = `resource://${_makeNamePath(name)}/js`;
     165                 :           0 :         } catch (e) {
     166                 :           0 :             moduledir = pkgdatadir;
     167                 :             :         }
     168                 :             :     }
     169                 :             : 
     170                 :           0 :     imports.searchPath.unshift(moduledir);
     171                 :           0 :     GIRepository.Repository.prepend_search_path(girpath);
     172                 :           0 :     GIRepository.Repository.prepend_library_path(libpath);
     173                 :             : 
     174                 :           0 :     try {
     175                 :           0 :         let resource = Gio.Resource.load(GLib.build_filenamev([pkgdatadir,
     176                 :           0 :             `${name}.data.gresource`]));
     177                 :           0 :         resource._register();
     178                 :           0 :     } catch (e) { }
     179                 :             : }
     180                 :             : 
     181                 :             : /**
     182                 :             :  * This is a convenience function if your package has a
     183                 :             :  * single entry point.
     184                 :             :  * You must define a main(ARGV) function inside a main.js
     185                 :             :  * module in moduledir.
     186                 :             :  *
     187                 :             :  * @param {object} params see init()
     188                 :             :  */
     189                 :           0 : function start(params) {
     190                 :           0 :     init(params);
     191                 :           0 :     run(imports.main);
     192                 :             : }
     193                 :             : 
     194                 :             : /**
     195                 :             :  * This is the function to use if you want to have multiple
     196                 :             :  * entry points in one package.
     197                 :             :  * You must define a main(ARGV) function inside the passed
     198                 :             :  * in module, and then the launcher would be
     199                 :             :  *
     200                 :             :  * imports.package.init(...);
     201                 :             :  * imports.package.run(imports.entrypoint);
     202                 :             :  *
     203                 :             :  * @param {object} module the module to run
     204                 :             :  * @returns {number|undefined} the exit code of the module's main() function
     205                 :             :  */
     206                 :           0 : function run(module) {
     207                 :           0 :     return module.main([System.programInvocationName].concat(ARGV));
     208                 :             : }
     209                 :             : 
     210                 :             : /**
     211                 :             :  * Mark a dependency on a specific version of one or more
     212                 :             :  * external GI typelibs.
     213                 :             :  * `libs` must be an object whose keys are a typelib name,
     214                 :             :  * and values are the respective version. The empty string
     215                 :             :  * indicates any version.
     216                 :             :  *
     217                 :             :  * @param {object} libs the external dependencies to import
     218                 :             :  */
     219                 :           0 : function require(libs) {
     220         [ #  # ]:           0 :     for (let l in libs)
     221                 :           0 :         requireSymbol(l, libs[l]);
     222                 :             : }
     223                 :             : 
     224                 :             : /**
     225                 :             :  * As checkSymbol(), but exit with an error if the
     226                 :             :  * dependency cannot be satisfied.
     227                 :             :  *
     228                 :             :  * @param {string} lib an external dependency to import
     229                 :             :  * @param {string} [ver] version of the dependency
     230                 :             :  * @param {string} [symbol] symbol to check for
     231                 :             :  */
     232                 :           0 : function requireSymbol(lib, ver, symbol) {
     233         [ #  # ]:           0 :     if (!checkSymbol(lib, ver, symbol)) {
     234         [ #  # ]:           0 :         if (symbol)
     235                 :           0 :             printerr(`Unsatisfied dependency: No ${symbol} in ${lib}`);
     236                 :             :         else
     237                 :           0 :             printerr(`Unsatisfied dependency: ${lib}`);
     238                 :           0 :         System.exit(1);
     239                 :             :     }
     240                 :             : }
     241                 :             : 
     242                 :             : /**
     243                 :             :  * Check whether an external GI typelib can be imported
     244                 :             :  * and provides @symbol.
     245                 :             :  *
     246                 :             :  * Symbols may refer to
     247                 :             :  *  - global functions         ('main_quit')
     248                 :             :  *  - classes                  ('Window')
     249                 :             :  *  - class / instance methods ('IconTheme.get_default' / 'IconTheme.has_icon')
     250                 :             :  *  - GObject properties       ('Window.default_height')
     251                 :             :  *
     252                 :             :  * @param {string} lib an external dependency to import
     253                 :             :  * @param {string} [ver] version of the dependency
     254                 :             :  * @param {string} [symbol] symbol to check for
     255                 :             :  * @returns {boolean} true if `lib` can be imported and provides `symbol`, false
     256                 :             :  * otherwise
     257                 :             :  */
     258                 :          20 : function checkSymbol(lib, ver, symbol) {
     259                 :          20 :     let Lib = null;
     260                 :             : 
     261         [ -  + ]:          20 :     if (ver)
     262                 :          20 :         imports.gi.versions[lib] = ver;
     263                 :             : 
     264                 :          20 :     try {
     265                 :          20 :         Lib = imports.gi[lib];
     266                 :           1 :     } catch (e) {
     267                 :           1 :         return false;
     268                 :             :     }
     269                 :             : 
     270         [ +  + ]:          19 :     if (!symbol)
     271                 :           1 :         return true; // Done
     272                 :             : 
     273 [ +  - ][ +  - ]:          18 :     let [klass, sym] = symbol.split('.');
         [ +  + ][ +  + ]
     274         [ +  + ]:          18 :     if (klass === symbol)
     275                 :           8 :         return typeof Lib[symbol] !== 'undefined';
     276                 :             : 
     277                 :          10 :     let obj = Lib[klass];
     278         [ +  - ]:          10 :     if (typeof obj === 'undefined')
     279                 :           0 :         return false;
     280                 :             : 
     281         [ +  + ]:          10 :     if (typeof obj[sym] !== 'undefined' ||
     282 [ +  + ][ +  + ]:           7 :         obj.prototype && typeof obj.prototype[sym] !== 'undefined')
     283                 :           5 :         return true; // class- or object method
     284                 :             : 
     285                 :             :     // GObject property
     286                 :           5 :     let pspec = null;
     287         [ +  + ]:           5 :     if (GObject.type_is_a(obj.$gtype, GObject.TYPE_INTERFACE)) {
     288                 :           1 :         let iface = GObject.type_default_interface_ref(obj.$gtype);
     289                 :           1 :         pspec = GObject.Object.interface_find_property(iface, sym);
     290         [ +  + ]:           4 :     } else if (GObject.type_is_a(obj.$gtype, GObject.TYPE_OBJECT)) {
     291                 :           3 :         pspec = GObject.Object.find_property.call(obj.$gtype, sym);
     292                 :             :     }
     293                 :             : 
     294                 :           5 :     return pspec !== null;
     295                 :          20 : }
     296                 :             : 
     297                 :           0 : function initGettext() {
     298                 :           0 :     Gettext.bindtextdomain(_pkgname, localedir);
     299                 :           0 :     Gettext.textdomain(_pkgname);
     300                 :             : 
     301                 :           0 :     let gettext = imports.gettext;
     302                 :           0 :     globalThis._ = gettext.gettext;
     303                 :           0 :     globalThis.C_ = gettext.pgettext;
     304                 :           0 :     globalThis.N_ = function (x) {
     305                 :           0 :         return x;
     306                 :             :     };
     307                 :             : }
     308                 :             : 
     309                 :           0 : function initFormat() {
     310                 :             :     // eslint-disable-next-line no-restricted-properties
     311                 :           0 :     let format = imports.format;
     312                 :           0 :     String.prototype.format = format.format;
     313                 :             : }
     314                 :             : 
     315                 :           0 : function initSubmodule(moduleName) {
     316 [ #  # ][ #  # ]:           0 :     if (_runningFromMesonSource() || _runningFromSource()) {
     317                 :             :         // Running from source tree, add './moduleName' to search paths
     318                 :             : 
     319                 :           0 :         let submoduledir = GLib.build_filenamev([_submoduledir, moduleName]);
     320                 :           0 :         let libpath;
     321         [ #  # ]:           0 :         if (_runningFromMesonSource())
     322                 :           0 :             libpath = submoduledir;
     323                 :             :         else
     324                 :           0 :             libpath = GLib.build_filenamev([submoduledir, '.libs']);
     325                 :           0 :         GIRepository.Repository.prepend_search_path(submoduledir);
     326                 :           0 :         GIRepository.Repository.prepend_library_path(libpath);
     327                 :             :     } else {
     328                 :             :         // Running installed, submodule is in $(pkglibdir), nothing to do
     329                 :             :     }
     330                 :             : }
        

Generated by: LCOV version 2.0-1