1# Copyright 2018 The Meson development team
2
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6
7#     http://www.apache.org/licenses/LICENSE-2.0
8
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15# This file contains the detection logic for external dependencies that
16# are UI-related.
17
18import json
19import os
20
21from . import ExtensionModule
22
23from .. import mlog
24
25from ..mesonlib import (
26    Popen_safe, MesonException
27)
28
29from ..dependencies.base import (
30    ExternalProgram, DubDependency
31)
32
33from ..interpreter import DependencyHolder
34
35class DlangModule(ExtensionModule):
36    class_dubbin = None
37    init_dub = False
38
39    def __init__(self, interpreter):
40        super().__init__(interpreter)
41        self.snippets.add('generate_dub_file')
42
43    def _init_dub(self):
44        if DlangModule.class_dubbin is None:
45            self.dubbin = DubDependency.class_dubbin
46            DlangModule.class_dubbin = self.dubbin
47        else:
48            self.dubbin = DlangModule.class_dubbin
49
50        if DlangModule.class_dubbin is None:
51            self.dubbin = self.check_dub()
52            DlangModule.class_dubbin = self.dubbin
53        else:
54            self.dubbin = DlangModule.class_dubbin
55
56        if not self.dubbin:
57            if not self.dubbin:
58                raise MesonException('DUB not found.')
59
60    def generate_dub_file(self, interpreter, state, args, kwargs):
61        if not DlangModule.init_dub:
62            self._init_dub()
63
64        if len(args) < 2:
65            raise MesonException('Missing arguments')
66
67        config = {
68            'name': args[0]
69        }
70
71        config_path = os.path.join(args[1], 'dub.json')
72        if os.path.exists(config_path):
73            with open(config_path, 'r', encoding='utf8') as ofile:
74                try:
75                    config = json.load(ofile)
76                except ValueError:
77                    mlog.warning('Failed to load the data in dub.json')
78
79        warn_publishing = ['description', 'license']
80        for arg in warn_publishing:
81            if arg not in kwargs and \
82               arg not in config:
83                mlog.warning('Without', mlog.bold(arg), 'the DUB package can\'t be published')
84
85        for key, value in kwargs.items():
86            if key == 'dependencies':
87                config[key] = {}
88                if isinstance(value, list):
89                    for dep in value:
90                        if isinstance(dep, DependencyHolder):
91                            name = dep.method_call('name', [], [])
92                            ret, res = self._call_dubbin(['describe', name])
93                            if ret == 0:
94                                version = dep.method_call('version', [], [])
95                                if version is None:
96                                    config[key][name] = ''
97                                else:
98                                    config[key][name] = version
99                elif isinstance(value, DependencyHolder):
100                    name = value.method_call('name', [], [])
101                    ret, res = self._call_dubbin(['describe', name])
102                    if ret == 0:
103                        version = value.method_call('version', [], [])
104                        if version is None:
105                            config[key][name] = ''
106                        else:
107                            config[key][name] = version
108            else:
109                config[key] = value
110
111        with open(config_path, 'w', encoding='utf8') as ofile:
112            ofile.write(json.dumps(config, indent=4, ensure_ascii=False))
113
114    def _call_dubbin(self, args, env=None):
115        p, out = Popen_safe(self.dubbin.get_command() + args, env=env)[0:2]
116        return p.returncode, out.strip()
117
118    def check_dub(self):
119        dubbin = ExternalProgram('dub', silent=True)
120        if dubbin.found():
121            try:
122                p, out = Popen_safe(dubbin.get_command() + ['--version'])[0:2]
123                if p.returncode != 0:
124                    mlog.warning('Found dub {!r} but couldn\'t run it'
125                                 ''.format(' '.join(dubbin.get_command())))
126                    # Set to False instead of None to signify that we've already
127                    # searched for it and not found it
128                    dubbin = False
129            except (FileNotFoundError, PermissionError):
130                dubbin = False
131        else:
132            dubbin = False
133        if dubbin:
134            mlog.log('Found DUB:', mlog.bold(dubbin.get_path()),
135                     '(%s)' % out.strip())
136        else:
137            mlog.log('Found DUB:', mlog.red('NO'))
138        return dubbin
139
140def initialize(*args, **kwargs):
141    return DlangModule(*args, **kwargs)
142