Salome HOME
use a repo_dev property to trigger the use of the dev repo, in addition to dev mode
[tools/sat.git] / commands / profile.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2013  CEA/DEN
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 import os
20 import shutil
21 import subprocess
22
23 import src
24
25 parser = src.options.Options()
26
27 parser.add_option( 'p', 'prefix', 'string', 'prefix', _("Where the profile's "
28                                                         "sources will be "
29                                                         "generated.") )
30 parser.add_option( 'n', 'name', 'string', 'name', _("Name of the profile's "
31                                                     "sources. [Default: "
32                                                     "${config.PRODUCT.name}"
33                                                     "_PROFILE]") )
34 parser.add_option( 'f', 'force', 'boolean', 'force', _("Overwrites "
35                                                        "existing sources.") )
36 parser.add_option( 'u', 'no_update', 'boolean', 'no_update', _("Does not update"
37                                                                " pyconf file."))
38 parser.add_option( 'v', 'version', 'string', 'version', _("Version of the "
39                                                           "application. [Defa"
40                                                           "ult: 1.0]"), '1.0' )
41 parser.add_option( 's', 'slogan', 'string', 'slogan', _("Slogan of the "
42                                                         "application.") )
43
44 ##################################################
45
46 ##
47 # Class that overrides common.Reference
48 # in order to manipulate fields starting with '@'
49 class profileReference( src.pyconf.Reference ) :
50     def __str__(self):
51         s = self.elements[0]
52         for tt, tv in self.elements[1:]:
53             if tt == src.pyconf.DOT:
54                 s += '.%s' % tv
55             else:
56                 s += '[%r]' % tv
57         if self.type == src.pyconf.BACKTICK:
58             return src.pyconf.BACKTICK + s + src.pyconf.BACKTICK
59         elif self.type == src.pyconf.AT:
60             return src.pyconf.AT + s
61         else:
62             return src.pyconf.DOLLAR + s
63
64 ##
65 # Class that overrides how fields starting with '@' are read.
66 class profileConfigReader( src.pyconf.ConfigReader ) :
67     def parseMapping(self, parent, suffix):
68         if self.token[0] == src.pyconf.LCURLY:
69             self.match(src.pyconf.LCURLY)
70             rv = src.pyconf.Mapping(parent)
71             rv.setPath(
72                src.pyconf.makePath(object.__getattribute__(parent, 'path'),
73                                    suffix))
74             self.parseMappingBody(rv)
75             self.match(src.pyconf.RCURLY)
76         else:
77             self.match(src.pyconf.AT)
78             __, fn = self.match('"')
79             rv = profileReference(self, src.pyconf.AT, fn)
80         return rv
81
82 ##################################################
83
84 ##
85 # Describes the command
86 def description():
87     return _("The profile command creates default profile.\nusage: sat profile "
88              "[PRODUCT] [-p|--prefix (string)] [-n|--name (string)] [-f|--force"
89              "] [-v|--version (string)] [-s|--slogan (string)]")
90
91 ##
92 # Gets the profile name
93 def get_profile_name ( options, config ):
94     if options.name :
95         res = options.name
96     else :
97         res = config.APPLICATION.name + "_PROFILE"
98     return res
99
100 ##
101 # Generates the sources of the profile
102 def generate_profile_sources( config, options, logger ):
103     #Check script app-quickstart.py exists
104     kernel_cfg = src.product.get_product_config(config, "KERNEL")
105     kernel_root_dir = kernel_cfg.install_dir
106     if not src.product.check_installation(kernel_cfg):
107         raise src.SatException(_("KERNEL is not installed"))
108     script = os.path.join(kernel_root_dir,"bin","salome","app-quickstart.py")
109     if not os.path.exists( script ):
110         raise src.SatException(_("KERNEL's install has not the script "
111                                  "app-quickstart.py"))
112
113     # Check that GUI is installed
114     gui_cfg = src.product.get_product_config(config, "GUI")
115     gui_root_dir = gui_cfg.install_dir
116     if not src.product.check_installation(gui_cfg):
117         raise src.SatException(_("GUI is not installed"))
118
119     #Set prefix option passed to app-quickstart.py
120     name = get_profile_name ( options, config )
121     prefix = os.path.join( options.prefix, name )
122     if os.path.exists( prefix ) :
123         if not options.force :
124             raise src.SatException( _("The path %s already exists, use option"
125                                       " --force to remove it." %prefix ) )
126         else :
127             shutil.rmtree( prefix )
128
129     #Set name option passed to app-quickstart.py
130     if name.upper().endswith("_PROFILE"):
131         name = name[:-8]
132
133     #Write command line that calls app-quickstart.py
134     command = "python %s --prefix=%s --name=%s --modules=_NO_ --version=%s" % (
135                                         script, prefix, name, options.version )
136     if options.force :
137         command += " --force"
138     if options.slogan :
139         command += " --slogan=%s" % options.slogan
140     logger.write("\n>" + command + "\n", 5, False)
141
142     #Run command
143     os.environ["KERNEL_ROOT_DIR"] = kernel_root_dir
144     os.environ["GUI_ROOT_DIR"] = gui_root_dir
145     res = subprocess.call(command,
146                     shell=True,
147                     env=os.environ,
148                     stdout=logger.logTxtFile,
149                     stderr=subprocess.STDOUT)
150     #Check result of command
151     if res != 0:
152         raise src.SatException(_("Cannot create application, code = %d\n")%res)
153     else:
154         logger.write(_("Profile sources were generated in directory %s.\n"%prefix),
155                      3)
156     return res
157
158 ##
159 # Updates the pyconf
160 def update_pyconf( config, options, logger ):
161
162     #Save previous version
163     pyconf = config.VARS.product + '.pyconf'
164     pyconfBackup = config.VARS.product + '-backup.pyconf'
165     logger.write(_("Updating %(new)s (previous version saved "
166                    "as %(old)s).") % { "new": pyconf, "old": pyconfBackup }, 3)
167     path = config.getPath( pyconf )
168     shutil.copyfile( os.path.join( path, pyconf ),
169                      os.path.join( path, pyconfBackup ) )
170
171     #Load config
172     cfg = src.pyconf.Config( )
173     object.__setattr__( cfg, 'reader', profileConfigReader( cfg ) )
174     cfg.load( src.pyconf.defaultStreamOpener( os.path.join( path, pyconf ) ) )
175
176     #Check if profile is in APPLICATION.products
177     profile = get_profile_name ( options, config )
178     if not profile in cfg.APPLICATION.products:
179         cfg.APPLICATION.products.append( profile, None )
180
181     #Check if profile is in APPLICATION
182     if not 'profile' in cfg.APPLICATION:
183         cfg.APPLICATION.addMapping( 'profile', src.pyconf.Mapping(), None )
184         cfg.APPLICATION.profile.addMapping( 'module', profile, None )
185         cfg.APPLICATION.profile.addMapping( 'launcher_name',
186                                             config.VARS.product.lower(), None )
187
188     #Check if profile info is in PRODUCTS
189     if not 'PRODUCTS' in cfg:
190         cfg.addMapping( 'PRODUCTS', src.pyconf.Mapping(), None )
191         
192     if not profile in cfg.PRODUCTS:
193         cfg.PRODUCTS.addMapping( profile, src.pyconf.Mapping(), None )
194         cfg.PRODUCTS[profile].addMapping( 'default', src.pyconf.Mapping(),
195                                           None )
196         prf = cfg.TOOLS.common.module_info[profile].default
197         prf.addMapping( 'name', profile, None )
198         prf.addMapping( 'get_source', 'archive', None )
199         prf.addMapping( 'build_source', 'cmake', None )
200         prf.addMapping( 'archive_info', src.pyconf.Mapping(), None )
201         prf.archive_info.addMapping( 'name',
202                                      os.path.join(os.path.abspath(options.prefix),
203                                                   profile ), None )
204         prf.addMapping( 'source_dir', src.pyconf.Reference(cfg,
205                                                            src.pyconf.DOLLAR,
206                                                            'APPLICATION.workdir'
207                                                            ' + $VARS.sep + "SOU'
208                                                            'RCES" + $VARS.sep +'
209                                                            ' $name' ), None )
210         prf.addMapping( 'build_dir', src.pyconf.Reference(cfg,
211                                                           src.pyconf.DOLLAR,
212                                                           'APPLICATION.workdir '
213                                                           '+ $VARS.sep + "BUILD'
214                                                           '" + $VARS.sep + $nam'
215                                                           'e' ), None )
216         prf.addMapping( 'depend', src.pyconf.Sequence(), None )
217         prf.depend.append( 'KERNEL', None )
218         prf.depend.append( 'GUI', None )
219         prf.depend.append( 'Python', None )
220         prf.depend.append( 'Sphinx', None )
221         prf.depend.append( 'qt', None )
222         prf.addMapping( 'opt_depend', src.pyconf.Sequence(), None )
223
224     #Save config
225     f = file( os.path.join( path, pyconf ) , 'w')
226     cfg.__save__(f)
227
228
229 ##
230 # Runs the command.
231 def run(args, runner, logger):
232     '''method that is called when salomeTools is called with profile parameter.
233     '''
234     (options, args) = parser.parse_args(args)
235     
236     src.check_config_has_application(runner.cfg)
237
238     if options.prefix is None:
239         msg = _("The --%s argument is required\n") % "prefix"
240         logger.write(src.printcolors.printcWarning(msg), 1)
241         return 1
242     
243     retcode = generate_profile_sources( runner.cfg, options, logger )
244
245     if not options.no_update :
246         update_pyconf( runner.cfg, options )
247
248     return retcode