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
22from .. import dependencies
23from .. import mlog
24from ..mesonlib import Popen_safe, MesonException
25
26class DlangModule(ExtensionModule):
27    class_dubbin = None
28    init_dub = False
29
30    def __init__(self, interpreter):
31        super().__init__(interpreter)
32        self.methods.update({
33            'generate_dub_file': self.generate_dub_file,
34        })
35
36    def _init_dub(self, state):
37        if DlangModule.class_dubbin is None:
38            self.dubbin = dependencies.DubDependency.class_dubbin
39            DlangModule.class_dubbin = self.dubbin
40        else:
41            self.dubbin = DlangModule.class_dubbin
42
43        if DlangModule.class_dubbin is None:
44            self.dubbin = self.check_dub(state)
45            DlangModule.class_dubbin = self.dubbin
46        else:
47            self.dubbin = DlangModule.class_dubbin
48
49        if not self.dubbin:
50            if not self.dubbin:
51                raise MesonException('DUB not found.')
52
53    def generate_dub_file(self, state, args, kwargs):
54        if not DlangModule.init_dub:
55            self._init_dub(state)
56
57        if len(args) < 2:
58            raise MesonException('Missing arguments')
59
60        config = {
61            'name': args[0]
62        }
63
64        config_path = os.path.join(args[1], 'dub.json')
65        if os.path.exists(config_path):
66            with open(config_path, encoding='utf-8') as ofile:
67                try:
68                    config = json.load(ofile)
69                except ValueError:
70                    mlog.warning('Failed to load the data in dub.json')
71
72        warn_publishing = ['description', 'license']
73        for arg in warn_publishing:
74            if arg not in kwargs and \
75               arg not in config:
76                mlog.warning('Without', mlog.bold(arg), 'the DUB package can\'t be published')
77
78        for key, value in kwargs.items():
79            if key == 'dependencies':
80                config[key] = {}
81                if isinstance(value, list):
82                    for dep in value:
83                        if isinstance(dep, dependencies.Dependency):
84                            name = dep.get_name()
85                            ret, res = self._call_dubbin(['describe', name])
86                            if ret == 0:
87                                version = dep.get_version()
88                                if version is None:
89                                    config[key][name] = ''
90                                else:
91                                    config[key][name] = version
92                elif isinstance(value, dependencies.Dependency):
93                    name = value.get_name()
94                    ret, res = self._call_dubbin(['describe', name])
95                    if ret == 0:
96                        version = value.get_version()
97                        if version is None:
98                            config[key][name] = ''
99                        else:
100                            config[key][name] = version
101            else:
102                config[key] = value
103
104        with open(config_path, 'w', encoding='utf-8') as ofile:
105            ofile.write(json.dumps(config, indent=4, ensure_ascii=False))
106
107    def _call_dubbin(self, args, env=None):
108        p, out = Popen_safe(self.dubbin.get_command() + args, env=env)[0:2]
109        return p.returncode, out.strip()
110
111    def check_dub(self, state):
112        dubbin = state.find_program('dub', silent=True)
113        if dubbin.found():
114            try:
115                p, out = Popen_safe(dubbin.get_command() + ['--version'])[0:2]
116                if p.returncode != 0:
117                    mlog.warning('Found dub {!r} but couldn\'t run it'
118                                 ''.format(' '.join(dubbin.get_command())))
119                    # Set to False instead of None to signify that we've already
120                    # searched for it and not found it
121                    dubbin = False
122            except (FileNotFoundError, PermissionError):
123                dubbin = False
124        else:
125            dubbin = False
126        if dubbin:
127            mlog.log('Found DUB:', mlog.bold(dubbin.get_path()),
128                     '(%s)' % out.strip())
129        else:
130            mlog.log('Found DUB:', mlog.red('NO'))
131        return dubbin
132
133def initialize(*args, **kwargs):
134    return DlangModule(*args, **kwargs)
135