1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4#-------------------------------------------------------------------------------
5
6# This file is part of Code_Saturne, a general-purpose CFD tool.
7#
8# Copyright (C) 1998-2021 EDF S.A.
9#
10# This program is free software; you can redistribute it and/or modify it under
11# the terms of the GNU General Public License as published by the Free Software
12# Foundation; either version 2 of the License, or (at your option) any later
13# version.
14#
15# This program is distributed in the hope that it will be useful, but WITHOUT
16# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18# details.
19#
20# You should have received a copy of the GNU General Public License along with
21# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
22# Street, Fifth Floor, Boston, MA 02110-1301, USA.
23
24#-------------------------------------------------------------------------------
25
26import fnmatch
27import os
28import sys
29import tempfile
30
31from optparse import OptionParser
32
33from cs_exec_environment import separate_args
34from cs_compile import cs_compile
35
36#-------------------------------------------------------------------------------
37
38def process_cmd_line(argv):
39    """
40    Processes the passed command line arguments for a build environment.
41
42    Input Argument:
43      arg -- This can be either a list of arguments as in
44             sys.argv[1:] or a string that is similar to the one
45             passed on the command line.  If it is a string,
46             it is split to create a list of arguments.
47    """
48
49    parser = OptionParser(usage="usage: %prog [options]")
50
51    parser.add_option("--mode", dest="mode", type="string",
52                      metavar="<mode>",
53                      help="'build' or 'install' mode")
54
55    parser.add_option("-d", "--dest", dest="dest_dir", type="string",
56                      metavar="<dest_dir>",
57                      help="choose executable file directory")
58
59    parser.add_option("-o", "--outfile", dest="out_file", type="string",
60                      metavar="<out_file>",
61                      help="choose executable file name")
62
63    parser.set_defaults(mode='build')
64    parser.set_defaults(dest_dir=None)
65    parser.set_defaults(out_file=None)
66
67    (options, args) = parser.parse_args(argv)
68
69    return options, args
70
71#-------------------------------------------------------------------------------
72
73def dest_subdir(destdir, d):
74
75    t = d
76
77    # Concatenate destdir and target subdirectory
78
79    if sys.platform.startswith("win"):
80        i = t.find(':\\')
81        if i > -1:
82            t = t[i+1:]
83            while t[0] == '\\':
84                t = t[1:]
85    else:
86        while t[0] == '/':
87            t = t[1:]
88
89    return os.path.join(destdir, t)
90
91#-------------------------------------------------------------------------------
92
93def src_include_dirs(srcdir):
94    """
95    Return include directories in a given source directory.
96    """
97    include_dirs = []
98
99    if srcdir:
100        for f in os.listdir(os.path.join(srcdir, 'src')):
101            p = os.path.join(srcdir, 'src', f)
102            if os.path.isdir(p):
103                include_dirs.append(p)
104
105    return include_dirs
106
107#===============================================================================
108# Class used to manage compilation in build directory
109#===============================================================================
110
111class compile_build(cs_compile):
112
113    def __init__(self,
114                 package=None,
115                 srcdir=None):
116        """
117        Initialize compiler object.
118        """
119        cs_compile.__init__(self, package)
120        self.srcdir = srcdir
121
122        top_builddir = os.getcwd()
123        while not os.path.isfile(os.path.join(top_builddir, "cs_config.h")):
124            ds = os.path.split(top_builddir)
125            if ds[1]:
126                top_builddir = ds[0]
127            else:
128                break
129
130        if not os.path.isdir(os.path.join(top_builddir, "src")):
131            raise Exception("top build directory not detected from: " \
132                            + os.getcwd())
133
134        self.top_builddir = top_builddir
135
136    #---------------------------------------------------------------------------
137
138    def get_compiler(self, compiler):
139        """
140        Determine the compiler path for a given compiler type.
141        """
142
143        return self.pkg.config.compilers[compiler]
144
145    #---------------------------------------------------------------------------
146
147    def flags_relocation(self, flag, cmd_line):
148
149        return
150
151    #---------------------------------------------------------------------------
152
153    def get_pkg_path_flags(self, flag):
154        """
155        Determine compilation flags for a given flag type.
156        """
157
158        flags = []
159
160        top_builddir = self.top_builddir
161
162        # Add CPPFLAGS and LDFLAGS information for the current package
163        if flag == 'cppflags':
164            if self.pkg.config.libs['ple'].variant == 'internal':
165                flags.append('-I' + os.path.join(top_builddir, 'libple'))
166                flags.append('-I' + os.path.join(self.srcdir, 'libple', 'src'))
167            flags.append('-I' + top_builddir)
168            include_dirs = src_include_dirs(self.srcdir)
169            for d in include_dirs:
170                flags.append('-I' + d)
171
172        elif flag == 'ldflags':
173            tsd = os.path.join(top_builddir, 'src')
174            for f in os.listdir(tsd):
175                p = os.path.join(tsd, f, '.libs')
176                if os.path.isdir(p):
177                    flags.append('-L' + p)
178            if self.pkg.config.libs['ple'].variant == 'internal':
179                flags.append('-L' + os.path.join(top_builddir, 'libple',
180                                                 'src', '.libs'))
181            l_cwd = '-L' + os.path.join(os.getcwd(), '.libs')
182            if not l_cwd in flags:
183                flags.append(l_cwd)
184            # Add library paths which may be indirectly required
185            re_lib_dirs = self.pkg.config.get_compile_dependency_paths()
186            for d in re_lib_dirs:
187                flags.insert(0, '-L' + d)
188
189        return flags
190
191    #---------------------------------------------------------------------------
192
193    def get_ar_lib_dir(self):
194        """
195        Determine directory containing library in archive mode.
196        """
197
198        return os.path.join(self.top_builddir, "src", "apps", ".libs")
199
200#===============================================================================
201# Class used to manage install
202#===============================================================================
203
204class compile_install(cs_compile):
205
206    def __init__(self,
207                 package=None,
208                 srcdir=None,
209                 destdir=None):
210        """
211        Initialize compiler object.
212        """
213        cs_compile.__init__(self, package)
214        self.srcdir = srcdir
215
216        self.destdir = destdir
217
218    #---------------------------------------------------------------------------
219
220    def get_pkg_path_flags(self, flag):
221        """
222        Determine compilation flags for a given flag type.
223        """
224
225        flags = []
226
227        # Add CPPFLAGS and LDFLAGS information for the current package
228        if flag == 'cppflags':
229            dirs = []
230            dirs.insert(0, self.pkg.dirs['pkgincludedir'])
231            if self.pkg.config.libs['ple'].variant == "internal":
232                dirs.insert(0, self.pkg.dirs['includedir'])
233            for d in dirs:
234                if self.destdir:
235                    flags.append("-I" + dest_subdir(self.destdir, d))
236                else:
237                    flags.append("-I" + d)
238
239        elif flag == 'ldflags':
240            # Do not use pkg.get_dir here as possible relocation paths must be
241            # used after installation, not before.
242            libdir = pkg.dirs['libdir']
243            # Strangely, on MinGW, Windows paths are not correctly handled here
244            # So, assuming we always build on MinGW, here is a little trick!
245            if sys.platform.startswith("win"):
246                if pkg.get_cross_compile() != 'cygwin': # mingw64
247                    libdir = os.path.normpath('C:\\MinGW\\msys\\1.0' + libdir)
248            if self.destdir:
249                libdir = dest_subdir(self.destdir, libdir)
250            flags.append("-L" + libdir)
251
252        return flags
253
254    #---------------------------------------------------------------------------
255
256    def get_compiler(self, compiler):
257        """
258        Determine the compiler path for a given compiler type.
259        """
260
261        return self.pkg.config.compilers[compiler]
262
263    #---------------------------------------------------------------------------
264
265    def flags_relocation(self, flag, cmd_line):
266
267        return
268
269    #---------------------------------------------------------------------------
270
271    def get_flags(self, flag):
272        """
273        Determine compilation flags for a given flag type.
274        """
275
276        if flag == 'libs':
277            return cs_compile.get_flags(self, flag)
278
279        cmd_line = self.get_pkg_path_flags(flag)
280
281        # Build the command line, and split possible multiple arguments in lists.
282        for lib in pkg.config.deplibs:
283            if (pkg.config.libs[lib].have == True \
284                and (not pkg.config.libs[lib].dynamic_load)):
285                cmd_line += separate_args(pkg.config.libs[lib].flags[flag])
286
287        if flag == 'ldflags':
288            # Do not use pkg.get_dir here as possible relocation paths
289            # must be used after installation, not before.
290            libdir = pkg.dirs['libdir']
291            # Strangely, on MinGW, Windows paths are not correctly
292            # handled here. So, assuming we always build on MinGW,
293            # here is a little trick!
294            if sys.platform.startswith("win"):
295                if pkg.get_cross_compile() != 'cygwin': # mingw64
296                    libdir = os.path.normpath('C:\\MinGW\\msys\\1.0' + libdir)
297            if self.destdir:
298                libdir = dest_subdir(self.destdir, libdir)
299            cmd_line.insert(0, "-L" + libdir)
300
301        return cmd_line
302
303    #---------------------------------------------------------------------------
304
305    def get_lib_dir(self):
306        """
307        Determine directory containing library.
308        """
309
310        cmd_line = self.get_pkg_path_flags(flag)
311
312        # Build the command line, and split possible multiple arguments in lists.
313        for lib in pkg.config.deplibs:
314            if (pkg.config.libs[lib].have == True \
315                and (not pkg.config.libs[lib].dynamic_load)):
316                cmd_line += separate_args(pkg.config.libs[lib].flags[flag])
317
318        print(cmd_line)
319        print(self.pkg.config.rpath)
320
321#===============================================================================
322# Functions
323#===============================================================================
324
325def install_exec_name(pkg, exec_name, destdir=None):
326    """
327    Determine full executable path and create associated directory
328    if necessary
329    """
330
331    exec_name = os.path.join(pkg.dirs['pkglibexecdir'], exec_name)
332    # Strangely, on MinGW, Windows paths are not correctly handled here...
333    # So, assuming we always build on MinGW, here is a little trick!
334    if sys.platform.startswith("win"):
335        if pkg.get_cross_compile() != 'cygwin': # mingw64
336            exec_name = os.path.normpath('C:\\MinGW\\msys\\1.0' + exec_name)
337        else:
338            exec_name = os.path.join(pkg.dirs['pkglibexecdir'],
339                                     os.path.basename(exec_name))
340    if destdir:
341        exec_name = dest_subdir(destdir, exec_name)
342    dirname = os.path.dirname(exec_name)
343    if not os.path.exists(dirname):
344        os.makedirs(dirname)
345
346    return exec_name
347
348#-------------------------------------------------------------------------------
349
350if __name__ == '__main__':
351
352    # Check mode and options
353    options, src_files = process_cmd_line(sys.argv[1:])
354
355    top_builddir = os.getenv("CS_TOP_BUILDDIR")
356    config_file_base = os.path.join("lib", "code_saturne_build.cfg")
357
358    if top_builddir:
359        top_builddir = os.path.abspath(top_builddir)
360        config_file = os.path.join(top_builddir, config_file_base)
361    else:
362        top_builddir = os.path.abspath(os.getcwd())
363        t = os.path.split(top_builddir)
364        while (t[1]):
365            config_file = os.path.join(top_builddir, config_file_base)
366            if os.path.isfile(config_file):
367                break;
368            t = os.path.split(top_builddir)
369            top_builddir = os.path.abspath(t[0])
370
371    # Retrieve package information (name, version, installation dirs, ...)
372
373    from cs_package import package
374
375    pkg = package(config_file=config_file, install_mode=True)
376
377    src_dir = None
378    if src_files:
379        src_dir = os.path.dirname(os.path.dirname(sys.argv[0]))
380
381    # Determine executable name
382
383    exec_name=options.out_file
384    if not exec_name:
385        exec_name = 'cs_solver'
386        if os.path.basename(sys.argv[0]) == 'neptune_cfd':
387            exec_name = 'nc_solver'
388
389    if options.mode == 'install':
390        c = compile_install(pkg, src_dir, destdir=options.dest_dir)
391        exec_name = install_exec_name(pkg, exec_name, options.dest_dir)
392    else:
393        c = compile_build(pkg, src_dir)
394
395    retcode = 0
396    o_files = None
397    if src_files:
398        retcode, o_files = c.compile_src(src_list=src_files)
399
400    if retcode == 0:
401        print("Linking executable: " + exec_name)
402        retcode = c.link_obj(exec_name, o_files)
403
404    sys.exit(retcode)
405
406#-------------------------------------------------------------------------------
407# End
408#-------------------------------------------------------------------------------
409