source: CMT/v1r18p20050901/source/cmt_cvs.cxx @ 91

Last change on this file since 91 was 91, checked in by arnault, 19 years ago

Fix bug when generating version.cmt file after checkout

  • Property svn:eol-style set to native
File size: 58.8 KB
Line 
1//-----------------------------------------------------------
2// Copyright Christian Arnault LAL-Orsay CNRS
3// arnault@lal.in2p3.fr
4// See the complete license in cmt_license.txt "http://www.cecill.info".
5//-----------------------------------------------------------
6
7#include <stdlib.h>
8#include <stdio.h>
9
10#include "cmt.h"
11#include "cmt_cvs.h"
12#include "cmt_awk.h"
13#include "cmt_symbol.h"
14#include "cmt_project.h"
15
16/**
17
18    Grep : perform a grep like operation onto a cmt_string
19
20    o All lines of the input string are collected when they contain
21      the specified pattern.
22    o The input string and the selector pattern are specified in
23      the constructor:
24
25         Grep (input_string, pattern)
26
27    o All selected lines are accumulated (appended) into the internal
28      variable m_result . 'space' is the separator.
29
30    o The accumulator is retrieved by the result () method.
31
32 */
33class Grep : public Awk
34{
35public:
36
37  void begin ();
38  void filter (const cmt_string& line);
39  const cmt_string& result () const;
40
41private:
42  cmt_string m_result;
43};
44
45/**
46
47     Cut : perform a cut-like operation :
48
49     o collect the <field>'th field of every line into the m_result
50       internal variable
51
52     o the field number is given in the constructor and starts at zero.
53
54     o selected fields are accumulated with a space as separator.
55
56 */
57class Cut : public Awk
58{
59public:
60  Cut (int field);
61  void begin ();
62  void filter (const cmt_string& line);
63  const cmt_string& result () const;
64
65private:
66  cmt_string m_result;
67  int m_field;
68};
69
70/**
71
72      History : maintains the history of checkouts during a
73                recursive checkout, so as to avoid double checkouts.
74
75 */
76class History
77{
78public:
79  static History& instance ();
80  void clear ();
81  void install (const cmt_string& line);
82  bool is_installed (const cmt_string& line);
83
84private:
85  History ();
86
87  cmt_string m_installed;
88};
89
90/**
91
92     RecursivePass1 : simply validate use statements in a requirements file
93                      and echo those that really need to be handled.
94
95 */
96class RecursivePass1 : public Awk
97{
98public:
99
100  void begin ();
101  void filter (const cmt_string& line);
102  const cmt_string& result () const;
103
104private:
105  cmt_string m_result;
106  bool m_first;
107};
108
109/**
110
111     RecursivePass2 : after filtering the use statements really perform the
112                      checkouts.
113
114 */
115class CvsImplementation;
116class RecursivePass2 : public Awk
117{
118public:
119  RecursivePass2 (CvsImplementation& cvs);
120  void begin ();
121  void filter (const cmt_string& line);
122
123private:
124  CvsImplementation& m_cvs;
125};
126
127/**
128
129     RecursivePass3 : simply validate use statements in a project file
130                      and echo those that really need to be handled.
131
132 */
133class RecursivePass3 : public Awk
134{
135public:
136
137  void begin ();
138  void filter (const cmt_string& line);
139  const cmt_string& result () const;
140
141private:
142  cmt_string m_result;
143  bool m_first;
144};
145
146/**
147
148     RecursivePass4 : after filtering the use statements really perform the
149                      project checkouts.
150
151 */
152class CvsImplementation;
153class RecursivePass4 : public Awk
154{
155public:
156  RecursivePass4 (CvsImplementation& cvs);
157  void begin ();
158  void filter (const cmt_string& line);
159
160private:
161  CvsImplementation& m_cvs;
162};
163
164/**
165
166   Internal implementation of CVS to CMT operations.
167   The Cvs class only provides abstract interface.
168
169 */
170class CvsImplementation
171{
172public:
173
174  CvsImplementation ()
175  {
176    clear ();
177  }
178
179  void clear ()
180  {
181    m_recursive  = false;
182    m_head       = false;
183    m_verbose    = false;
184    m_simulation = false;
185
186    m_home_dir     = "";
187    m_checkout_dir = "";
188    m_version_dir  = "";
189    m_cvs_offset   = "";
190
191    m_last_module        = "";
192    m_last_cvs_infos     = "";
193    structure_info       = "";
194    error_info           = "";
195    tags_top_info        = "";
196    tags_info            = "";
197    cvsversions_top_info = "";
198    cvsversions_info     = "";
199    branches_info        = "";
200    subpackages_info     = "";
201    subprojects_info     = "";
202
203    m_protocol_level     = "";
204
205    Symbol* symbol = Symbol::find ("cmt_cvs_protocol_level");
206    if (symbol != 0)
207      {
208        m_protocol_level = symbol->build_macro_value ();
209        Symbol::expand (m_protocol_level);
210      }
211  }
212
213  CvsImplementation& operator = (const CvsImplementation& other)
214  {
215    m_recursive  = other.m_recursive;
216    m_head       = other.m_head;
217    m_verbose    = other.m_verbose;
218    m_simulation = other.m_simulation;
219
220    m_home_dir       = other.m_home_dir;
221    m_checkout_dir   = other.m_checkout_dir;
222    m_version_dir    = other.m_version_dir;
223    m_cvs_offset     = other.m_cvs_offset;
224    m_protocol_level = other.m_protocol_level;
225    m_last_module    = other.m_last_module;
226    m_last_cvs_infos = other.m_last_cvs_infos;
227
228    structure_info       = other.structure_info;
229    error_info           = other.error_info;
230    tags_top_info        = other.tags_top_info;
231    tags_info            = other.tags_info;
232    cvsversions_top_info = other.cvsversions_top_info;
233    cvsversions_info     = other.cvsversions_info;
234    branches_info        = other.branches_info;
235    subpackages_info     = other.subpackages_info;
236    subprojects_info     = other.subprojects_info;
237
238    return (*this);
239  }
240
241  /**
242     Filter out the space-separated words of a text that don't match a regexp.
243   */
244  void filter_list (cmt_string& text, const cmt_regexp& exp)
245  {
246    CmtSystem::cmt_string_vector list;
247
248    CmtSystem::split (text, " ", list);
249
250    int i;
251
252    text = "";
253
254    for (i = 0; i < list.size (); i++)
255      {
256        const cmt_string& s = list[i];
257        if (exp.match (s))
258          {
259            if (i > 0) text += " ";
260            text += s;
261          }
262      }
263  }
264
265  int execute (const cmt_string& command)
266  {
267    int status = 0;
268
269    if (m_verbose || m_simulation)
270      {
271        cout << "#CMT> Executing [" << command << "]" << endl;
272      }
273   
274    if (!m_simulation)
275      {
276        status = CmtSystem::execute (command);
277      }
278
279    return (status);
280  }
281
282  void execute_and_retry (const cmt_string& command, const cmt_string& message)
283  {
284    int status;
285    int retry = 0;
286
287    for (;;)
288      {
289        status = execute (command);
290       
291        if (status != 0)
292          {
293            retry++;
294           
295            cout << "# " << message << ": status=" << status << endl;
296            cout << "#---------------------------------------------------------" << endl;
297           
298            if (retry > 5) exit(0);
299          }
300        else
301          {
302            break;
303          }
304      }
305  }
306
307  int execute (const cmt_string& command, cmt_string& out)
308  {
309    int status = 0;
310
311    if (m_verbose || m_simulation)
312      {
313        cout << "#CMT> Executing [" << command << "]" << endl;
314      }
315   
316    if (!m_simulation)
317      {
318        status = CmtSystem::execute (command, out);
319      }
320
321    return (status);
322  }
323
324
325  /**
326     This function will check the protocol level with the CVS pluggin.
327     The expected level is defined in a macro (in the requirements file of CMT)
328     The protocol level characterizes the structure of the message received from the CVS pluggin
329     and depends on its version.
330
331     In order to know if the pluggin is installed AND if the support for this version is
332     installed, we checkout CVSROOT/loginfo which should contain entries with selection patterns
333
334     .cmtcvsinfos/<protocol_level>
335
336     However old versions only offer as pattern:
337
338     .cmtcvsinfos/
339
340     In this function we'll detect if the effective protocol level satisifies the
341     level expected for this release of CMT
342
343     In simulation mode we suppose the expected protocol has been found.
344   */
345  bool check_protocol ()
346  {
347    static bool done = false;
348    static bool found = true;
349
350    if (done) return (found);
351    done = true;
352
353    cmt_string cvsroot;
354       
355    CmtSystem::get_cvsroot (cvsroot);
356       
357    cmt_string command;
358       
359    command = "cvs";
360    if (cvsroot != "") 
361      {
362        command += " -d ";
363        command += cvsroot;
364      }
365    command += " -Q co -p CVSROOT/loginfo ";
366
367    found = false;
368
369    cmt_string pattern = ".cmtcvsinfos/";
370    pattern += m_protocol_level;
371
372    cmt_string loginfo;
373
374    if (m_simulation)
375      {
376        loginfo = pattern;
377      }
378
379    execute (command, loginfo);
380
381    int pos = loginfo.find (pattern);
382
383    if (pos != cmt_string::npos)
384      {
385        found = true;
386      }
387
388    if (m_verbose)
389      {
390        if (found)
391          {
392            cout << "#CMT> Protocol level " << m_protocol_level << endl;
393          }
394        else
395          {
396            cout << "#CMT> The CVS pluggin is not installed or is not at protocol level " << m_protocol_level << endl;
397          }
398      }
399   
400    return (found);
401  }
402
403  /**
404     Execute the CVS command that activates the CVS pluggin, ie this is a cvs import
405     using the conventional module .cmtcvsinfos/<protocol-level>/<module>
406
407     We create a temporary directory just to lauch the command. However nothing
408     should change in this temporary directory since the pluggin returns an error status.
409   */
410  void retreive_cvs_infos (const cmt_string& module)
411  {
412    static const cmt_string cmtcvsinfos = ".cmtcvsinfos";
413
414    cmt_string home_dir = CmtSystem::pwd ();
415       
416    //
417    // Activities related with .cmtcvsinfos will occur in a temporary directory
418    //
419    cmt_string tmp_dir = CmtSystem::getenv ("TMPDIR");
420    if (tmp_dir == "")
421      {
422        tmp_dir = CmtSystem::file_separator ();
423        tmp_dir += "tmp";
424      }
425   
426    if (!CmtSystem::cd (tmp_dir))
427      {
428        tmp_dir = home_dir;
429      }
430   
431    tmp_dir += CmtSystem::file_separator ();
432    tmp_dir += "cmtcvs";
433    {
434      cmt_string temp = CmtSystem::get_temporary_name ();
435      CmtSystem::basename (temp, temp);
436      // Suppress dots for Windows
437      temp.replace_all (".", "");
438      tmp_dir += temp;
439    }
440   
441    if (!CmtSystem::test_directory (tmp_dir))
442      {
443        if (!CmtSystem::mkdir (tmp_dir))
444          {
445            cout << "#CMT> Cannot create the temporary directory [" << tmp_dir << "]" << endl;
446            return;
447          }
448      }
449   
450    //trap "rm -rf ${tmp_dir}" 0 1 2 15
451   
452    if (!CmtSystem::cd (tmp_dir))
453      {
454        cout << "#CMT> Cannot move to the temporary directory " << tmp_dir << endl;
455       
456        if (m_verbose)
457          {
458            cout << "#CMT> now removing tmp_dir " << tmp_dir << " home=" << home_dir << endl;
459          }
460       
461        CmtSystem::remove_directory (tmp_dir);
462       
463        return;
464      }
465   
466    if (m_verbose)
467      {
468        cout << "#CMT> cvs infos are now obtained from the temporary directory " << CmtSystem::pwd () << endl; 
469      }
470   
471    /**
472       The script associated to such entries is supposed to :
473        1) extract the set of <infos> from ${module}/cmt/requirements
474                                        or ${module}/cmt/project.cmt
475        2) build an output of the form :
476            <infos>=info1 info2 info3 ...
477     
478       Currently this script can be found in
479     
480       ${CMTROOT}/cmt/cmt_buildcvsinfos2.sh
481       %CMTROOT%/cmt/cmt_buildcvsinfos.py
482       There is a C++ implementation as cmtcvs.exe
483     
484    */
485   
486    if (!CmtSystem::test_directory (cmtcvsinfos))
487      {
488        CmtSystem::mkdir (cmtcvsinfos);
489      }
490   
491    CmtSystem::cd (cmtcvsinfos);
492   
493    cmt_string cvsroot;
494   
495    CmtSystem::get_cvsroot (cvsroot);
496   
497    cmt_string command;
498   
499    command = "cvs";
500    if (cvsroot != "") 
501      {
502        command += " -d ";
503        command += cvsroot;
504      }
505    command += " -Q import -m cmt ";
506   
507    command += cmtcvsinfos;
508   
509    if (m_protocol_level != "")
510      {
511        command += "/";
512        command += m_protocol_level;
513      }
514    command += "/";
515    command += module;
516    command += " CMT v1";
517   
518    m_last_cvs_infos = "";
519
520    execute (command, m_last_cvs_infos);
521   
522    if (m_verbose)
523      {
524        cout << "#CMT> now removing tmp_dir " << tmp_dir << " home=" << home_dir << endl;
525      }
526   
527    CmtSystem::cd (home_dir);
528    CmtSystem::remove_directory (tmp_dir);     
529  }
530
531  /**
532     This method exploits the hook installed into the loginfo script.
533     A communication is setup with a dummy CVS module named .cmtcvsinfos/<module>
534   
535     At import time, the contents of the file will be used to parameterize
536     the script named cmt_buildcvsinfos2.sh (referenced in the loginfo script)
537   
538      This script performs a scan in the CVS repository for the following types of
539     information :
540   
541       the recognized structure below this module (none, project, package)
542       all top symbolic tags installed for the module
543       all symbolic tags installed for the module
544       all branches available below this module
545       all subpackages installed below the module.
546       all subprojects installed below the module.
547   
548        In principle, only modules corresponding to true CMT products (packages or projects) are considered.
549          o tags are obtained from the requirements or the project file
550          o branches are sub-directories which are not themselves packages
551          o subpackages are sub-directories which are CMT packages
552            (a subdirectory is always either a branch or a subpackage)
553  */
554  void get_cvs_infos_with_offset (const cmt_string& module)
555  {
556    if (!check_protocol ())
557      {
558        cout << "#CMT> The CVS pluggin is not installed or is not at protocol level " << m_protocol_level << endl;
559        return;
560      }
561
562    if (module == "")
563      {
564        cout << "#CMT> cmt cvs needs a module name" << endl;
565        return;
566      }
567
568    if (module == m_last_module)
569      {
570        if (m_verbose)
571          {
572            cout << "#CMT> cvs infos for module " << module << " already there" << endl;
573          }
574      }
575    else
576      {
577        m_last_module = module;
578       
579        retreive_cvs_infos (module);
580      }
581   
582    /**
583       Now retrieve all info fields :
584    */
585
586    Grep grep;
587
588    grep.run (m_last_cvs_infos, "structure=");
589   
590    if (grep.result () != "")
591      {
592        structure_info = grep.result ();
593        structure_info.replace ("structure=", "");
594      }
595    else
596      {
597        // This may happen for old protocol level < v1r1
598        structure_info = "package";
599      }
600   
601    grep.run (m_last_cvs_infos, "error=");
602   
603    if (grep.result () != "")
604      {
605        error_info = grep.result ();
606        error_info.replace ("error=", "");
607      }
608    else
609      {
610        error_info = "";
611      }
612   
613    grep.run (m_last_cvs_infos, "tags_top=");
614   
615    if (grep.result () != "")
616      {
617        tags_top_info = grep.result ();
618        tags_top_info.replace ("tags_top=", "");
619      }
620    else
621      {
622        tags_top_info = "";
623      }
624   
625    grep.run (m_last_cvs_infos, "tags=");
626   
627    if (grep.result () != "")
628      {
629        tags_info = grep.result ();
630        tags_info.replace ("tags=", "");
631      }
632    else
633      {
634        tags_info = "";
635      }
636   
637    grep.run (m_last_cvs_infos, "cvsversions_top=");
638   
639    if (grep.result () != "")
640      {
641        cvsversions_top_info = grep.result ();
642        cvsversions_top_info.replace ("cvsversions_top=", "");
643      }
644    else
645      {
646        cvsversions_top_info = "";
647      }
648   
649    grep.run (m_last_cvs_infos, "cvsversions=");
650   
651    if (grep.result () != "")
652      {
653        cvsversions_info = grep.result ();
654        cvsversions_info.replace ("cvsversions=", "");
655      }
656    else
657      {
658        cvsversions_info = "";
659      }
660   
661    grep.run (m_last_cvs_infos, "branches=");
662   
663    if (grep.result () != "")
664      {
665        branches_info = grep.result ();
666        branches_info.replace ("branches=", "");
667      }
668    else
669      {
670        branches_info = "";
671      }
672   
673    grep.run (m_last_cvs_infos, "subpackages=");
674   
675    if (grep.result () != "")
676      {
677        subpackages_info = grep.result ();
678        subpackages_info.replace ("subpackages=", "");
679      }
680    else
681      {
682        subpackages_info = "";
683      }
684   
685    grep.run (m_last_cvs_infos, "subprojects=");
686
687    if (grep.result () != "")
688      {
689        subprojects_info = grep.result ();
690        subprojects_info.replace ("subprojects=", "");
691      }
692    else
693      {
694        subprojects_info = "";
695      }
696
697    /**
698       The CMTCVSTAGFILTER env. var. may contain a regexp that will exclude some tags
699       from the answr of the CVS pluggin.
700       The pattern is a regexp but it may also contain the <package> template
701     */
702    cmt_string tag_filter = CmtSystem::getenv ("CMTCVSTAGFILTER");
703   
704    if (tag_filter != "")
705      {
706        cmt_string package;
707        CmtSystem::basename (module, package);
708       
709        cmt_string pattern = "<package>";
710       
711        tag_filter.replace_all (pattern, package);
712       
713        cmt_regexp exp (tag_filter);
714       
715        cmt_string text;
716       
717        filter_list (tags_top_info, exp);
718        filter_list (tags_info, exp);
719        filter_list (cvsversions_top_info, exp);
720        filter_list (cvsversions_info, exp);
721      }
722  }
723 
724  void get_cvs_infos (const cmt_string& cvs_offset, const cmt_string& module)
725  {
726    cmt_string full_name;
727
728    if (cvs_offset != "") 
729      {
730        full_name = cvs_offset;
731        full_name += "/";
732        while (full_name.find ("//") != cmt_string::npos)
733          {
734            full_name.replace_all ("//", "/");
735          }
736      }
737
738    full_name += module;
739
740    get_cvs_infos_with_offset (full_name);
741  }
742
743  /**
744     From a space-separated list of version tags, try to find one tag
745     matching a given regular expression.
746
747       o The first matching tag is returned into 'version'
748       o Success is returned as function value.
749
750   */
751  bool match_version_request (const cmt_string& text, 
752                              const cmt_regexp& version_exp,
753                              cmt_string& version)
754  {
755    CmtSystem::cmt_string_vector vs;
756     
757    CmtSystem::split (text, " \t", vs);
758
759    version = "";
760
761    for (int i = 0; i < vs.size (); i++)
762      {
763        const cmt_string& vv = vs[i];
764       
765        if (version_exp.match (vv))
766          {
767            version = vv;
768            return (true);
769          }
770      }
771
772    return (false);
773  }
774
775  void get_module (const cmt_string& offset,
776                   const cmt_string& product,
777                   cmt_string& module)
778  {
779    module = "";
780
781    if (offset != "")
782      {
783        module = offset;
784        module += "/"; // This is for CVS only thus we don't use the real separator.
785        while (module.find ("//") != cmt_string::npos)
786          {
787            module.replace_all ("//", "/");
788          }
789      }
790
791    module += product;
792  }
793
794  bool get_version (const cmt_string& offset,
795                    const cmt_string& product,
796                    const cmt_string& version_request,
797                    const cmt_string& module,
798                    cmt_string& version,
799                    bool& at_head)
800  {
801    Grep grep;
802    cmt_string topversions;
803    cmt_string versions;
804    cmt_string requested_version = version_request;
805   
806    at_head = false;
807           
808    /**
809     *   Try to figure out what is the effective version tag available
810     *   for the requested version expressions (which may contain
811     *   wild card)
812     *
813     *     the requested version may either be in the top tags (ie those
814     *     corresponding to the same most recent CVS version number before
815     *     the HEAD) or not. The returned at_head flag will show this.
816     *
817     *     then the requested product may either be located in the CVS repository
818     *     under the offset or not, the returned module will contain the effective
819     *     location where the requested version has been found.
820     */
821   
822    if (m_verbose)
823      {
824        cout << "#CMT> requesting cvs infos onto module " << module << endl;
825      }
826   
827    get_cvs_infos_with_offset (module);
828   
829    if (error_info != "")
830      {
831        versions = "";
832        cout << "#CMT> Product " << product << " not found in ${CVSROOT}" << endl;
833        return (false);
834      }
835
836    versions = tags_top_info;
837
838    cmt_string v = version_request;
839   
840    if (version_request.find ("*") != cmt_string::npos)
841      {
842        v.replace_all ("*", ".*");
843      }
844    else
845      {
846        // this is an exact match to the end of the word since there is no wild card
847        v += "$";
848      }
849   
850    cmt_regexp version_exp (v);
851   
852    if (!match_version_request (versions, version_exp, version))
853      {
854        // We try on non-top versions
855
856        versions = tags_info;
857
858        if (!match_version_request (versions, version_exp, version))
859          {
860            version = requested_version;
861            int pos = 0;
862            if ((pos = version.find ("*")) != cmt_string::npos)
863              {
864                //
865                //  There was a wild card but the expression does not match
866                // any of the existing tags in CVS.
867                //  Things will be retreived from HEAD but we have to build
868                // a reasonable version tag from the wild card expression.
869                //  If the letter before the * was a digit, then simply remove
870                // the * (v5* -> v5) otherwise add a zero (v5r* -> v5r0)
871                //
872                if (pos > 0)
873                  {
874                    char letter = version[pos-1];
875                   
876                    static const cmt_string digits = "0123456789";
877                   
878                    if (digits.find (letter) == cmt_string::npos)
879                      {
880                        // "v5r*" -> "v5r0"
881                        version.replace ("*", "0");
882                      }
883                    else
884                      {
885                        // "v5*" -> "v5"
886                        version.replace ("*", "");
887                      }
888                  }
889                else
890                  {
891                    // The expression was simply "*" !!!
892                    version = "v0";
893                  }
894              }
895            at_head = true;
896          }
897        else
898          {
899            at_head = false;
900          }
901      }
902    else
903      {
904        at_head = true;
905      }
906   
907    /**
908     *   Here we have at least one version matching the requested expression.
909     */
910   
911    return (true);
912  }
913
914  bool do_need_version ()
915  {
916    bool need_version = false;
917
918    if (structure_info == "project")
919      {
920        need_version = true;
921      }
922    else
923      {
924        CmtStructuringStyle style = Cmt::get_current_structuring_style ();
925
926        if (style == default_structuring_style)
927          {
928            Use& current_use = Use::current ();
929            if (current_use.get_strategy ("VersionDirectory"))
930              {
931                need_version = true;
932              }
933          }
934        else if (style == with_version_directory)
935          {
936            need_version = true;
937          }
938      }
939
940    return (need_version);
941  }
942
943  /**
944     Take care of structuring style for packages and of project vs package conventions
945   */
946  cmt_string build_version_directory (const cmt_string& offset,
947                                      const cmt_string& product,
948                                      const cmt_string& version)
949  {
950    cmt_string dir = m_home_dir;
951   
952    if (m_checkout_dir != "")
953      {
954        // consider the usual -d option
955 
956        dir += CmtSystem::file_separator ();
957        dir += m_checkout_dir;
958      }
959   
960    dir += CmtSystem::file_separator ();
961    dir += offset;
962    dir += CmtSystem::file_separator ();
963    dir += product;
964
965    if (do_need_version ())
966      {
967        dir += CmtSystem::file_separator ();
968        dir += version;
969      }
970   
971    CmtSystem::reduce_file_separators (dir);
972   
973    return (dir);
974  }
975
976  /**
977     Wrapper to mkdir handling simulation and verbose options.
978   */
979  bool mkdir (const cmt_string& dir)
980  {
981    if (m_simulation)
982      {
983        cout << "#CMT> Would create the " << dir << " directory" << endl;
984      }
985    else
986      {
987        if (!CmtSystem::cd (dir))
988          {
989            if (m_verbose)
990              {
991                cout << "#CMT> About to mkdir " << dir << endl;
992              }
993
994            CmtSystem::mkdir (dir);
995            if (!CmtSystem::cd (dir))
996              {
997                cout << "# Error creating the directory :" << dir << endl;
998                cout << "#---------------------------------------------------------" << endl;
999                return (false);
1000              }
1001          }
1002      }
1003    return (true);
1004  }
1005
1006  /**
1007     When running cmt cvs commands, we stand by definition outside of any existing
1008     package context. Thus it's likely that CMTPATH are not completely defined.
1009     This function manually prepends CMTPATH entries to the environment variable.
1010   */
1011  void add_cmtpath (const cmt_string& dir)
1012  {
1013    static cmt_string CMTPATH;
1014
1015    cmt_string cmtpath = CmtSystem::getenv ("CMTPATH");
1016
1017    if (cmtpath.find (dir) == cmt_string::npos)
1018      {
1019        CMTPATH = "CMTPATH=";
1020        CMTPATH += dir;
1021        CMTPATH += ":";
1022        CMTPATH += cmtpath;
1023       
1024        CmtSystem::putenv (CMTPATH);
1025      }
1026
1027    if (m_verbose)
1028      {
1029        cout << "#CMT> CMTPATH=" << CmtSystem::getenv ("CMTPATH") << endl;
1030      }
1031  }
1032
1033  /**
1034     Construct CVS management files in the top directory. This is needed
1035     if the top directory of a product is empty. (In this case the
1036     co -l results in nothing)
1037   */
1038  void make_management_files (const cmt_string& module,
1039                              const cmt_string& entries_text)
1040  {
1041    if (!CmtSystem::test_directory ("CVS"))
1042      {
1043        /**
1044         * The CVS repository had not been created (this is generally
1045         * due to the lack of top files)
1046         */
1047
1048        if (!mkdir ("CVS")) return;
1049
1050        CmtSystem::cd ("..");
1051
1052        cmt_string s;
1053       
1054        // Let's create first the CVS/Root file.
1055       
1056        CmtSystem::get_cvsroot (s);
1057        s += "\n";
1058       
1059        cmt_string f;
1060       
1061        f = "CVS";
1062        f += CmtSystem::file_separator ();
1063        f += "Root";
1064       
1065        if (m_simulation)
1066          {
1067            cout << "#CMT> Would fill in the CVS/Root file with " << endl;
1068            cout << s << endl;
1069          }
1070        else
1071          {
1072            if (m_verbose)
1073              {
1074                cout << "#CMT> Fill in the CVS/Root file with " << endl;
1075                cout << s << endl;
1076              }
1077            s.write (f);
1078          }
1079       
1080        // Now we create the CVS/Repository file
1081       
1082        f = "CVS";
1083        f += CmtSystem::file_separator ();
1084        f += "Repository";
1085       
1086        CmtSystem::get_cvsroot (s);
1087        if (s[0] == ':')
1088          {
1089            int pos = s.find (1, ":");
1090            s.erase (0, pos+1);
1091            pos = s.find (0, ":");
1092            s.erase (0, pos+1);
1093          }
1094        s += "/";
1095        s += module;
1096        s += "\n";
1097       
1098        if (m_simulation)
1099          {
1100            cout << "#CMT> Would fill in the CVS/Repository file with " << endl;
1101            cout << s << endl;
1102          }
1103        else
1104          {
1105            if (m_verbose)
1106              {
1107                cout << "#CMT> Fill in the CVS/Repository file with " << endl;
1108                cout << s << endl;
1109              }
1110            s.write (f);
1111          }
1112      }
1113   
1114    if (m_simulation)
1115      {
1116        cout << "#CMT> Would write the top CVS/Entries file with " << endl;
1117        cout << entries_text << endl;
1118      }
1119    else
1120      {
1121        cmt_string entries_file_name;
1122
1123        entries_file_name = "CVS";
1124        entries_file_name += CmtSystem::file_separator ();
1125        entries_file_name += "Entries";
1126   
1127        cmt_string text;
1128
1129        if (!text.read (entries_file_name))
1130          {
1131            // This happens when there were no top files
1132          }
1133
1134        text += entries_text;
1135
1136        // Now the CVS/Entries is ready to be created.
1137        if (m_verbose)
1138          {
1139            cout << "#CMT> Fill in the top CVS/Entries file with " << endl;
1140            cout << text << endl;
1141          }
1142
1143        text.write (entries_file_name);
1144      }
1145
1146  }
1147
1148  /**
1149     Specific checkout of one project
1150   */
1151  bool really_checkout_project_contents (const cmt_string& offset,
1152                                         const cmt_string& project,
1153                                         const cmt_string& version,
1154                                         const cmt_string& tag,
1155                                         const cmt_string& module,
1156                                         const cmt_string& basedir,
1157                                         bool at_head,
1158                                         const cmt_string& currentdir)
1159  {
1160    cmt_string dir = currentdir;
1161
1162    cout << "  # get project files into " << dir << endl;
1163
1164    cmt_string version_dir = version;
1165
1166    if (!mkdir (version_dir)) return (false);
1167
1168    dir += CmtSystem::file_separator ();
1169    dir += version_dir;
1170   
1171    cmt_string command = "cvs -Q co -P ";
1172    if (!at_head)
1173      {
1174        command += "-r ";
1175        command += (tag != "") ? tag : version;
1176      }
1177
1178    command += " -d cmt ";
1179
1180    command += " ";
1181    command += module;
1182    command += "/cmt";
1183
1184    execute_and_retry (command, "Error getting project CMT contents");
1185
1186    make_management_files (module, "D/cmt////\n");
1187
1188    return (true);
1189  }
1190
1191  /**
1192     Specific checkout of one package
1193
1194     1) get top files (no directories)
1195     2) construct the directory structure (with or without version directory)
1196     3) build the CVS/Entries file for subdirs and co individual subdirs
1197     4) write the CVS management files if CVS did not do it.
1198   */
1199  bool really_checkout_package_contents (const cmt_string& offset,
1200                                         const cmt_string& package,
1201                                         const cmt_string& version,
1202                                         const cmt_string& module,
1203                                         const cmt_string& basedir,
1204                                         bool at_head,
1205                                         const cmt_string& currentdir)
1206  {
1207    cmt_string dir = currentdir;
1208
1209    cout << "  # get top files " << endl;
1210           
1211    cmt_string command = "cvs -Q co -P -l ";
1212    if (!at_head)
1213      {
1214        command += "-r ";
1215        command += version;
1216      }
1217
1218    bool need_version = do_need_version ();
1219
1220    if (need_version)
1221      {
1222        command += " -d ";
1223        command += version;
1224      }
1225    else
1226      {
1227        command += " -d ";
1228        command += package;
1229
1230        // Must stand just above the package directory
1231        CmtSystem::cd ("..");
1232        CmtSystem::dirname (dir, dir);
1233      }
1234   
1235    command += " ";
1236    command += module;
1237   
1238    execute_and_retry (command, "Error getting package CMT contents");
1239
1240    if (need_version)
1241      {
1242        if (!mkdir (version)) return (false);
1243       
1244        dir += CmtSystem::file_separator ();
1245        dir += version;
1246      }
1247    else
1248      {
1249        if (!mkdir (package)) return (false);
1250       
1251        dir += CmtSystem::file_separator ();
1252        dir += package;
1253      }
1254   
1255    if (m_verbose)
1256      {
1257        cout << "#CMT> Now getting subdirectories pwd=" << CmtSystem::pwd () << " dir=" << dir << endl;
1258      }
1259
1260    cmt_string branches = CmtSystem::getenv ("CMTCVSBRANCHES");
1261   
1262    if (branches == "")
1263      {
1264        branches = branches_info;
1265      }
1266   
1267    CmtSystem::cmt_string_vector branch_vector;
1268   
1269    CmtSystem::split (branches, " \t", branch_vector);
1270   
1271    cout << "  # get branches " << branches << endl;
1272   
1273    cmt_string text = "";
1274   
1275    command = "";
1276   
1277    int i;
1278   
1279    for (i = 0; i < branch_vector.size (); i++)
1280      {
1281        cmt_string& branch = branch_vector[i];
1282       
1283        if (i > 0)
1284          {
1285            command += CmtSystem::command_separator ();
1286          }
1287       
1288        command += "cvs -Q co ";
1289       
1290        if (!at_head)
1291          {
1292            command += "-r ";
1293            command += version;
1294          }
1295       
1296        command += " -d ";
1297        command += branch;
1298        command += " ";
1299        command += module;
1300        command += "/";    // CVS uses the '/' notation on all platforms!!
1301        command += branch;
1302       
1303        text += "D/";
1304        text += branch;
1305        text += "////\n";
1306      }
1307   
1308    execute_and_retry (command, "Error getting package contents");
1309
1310    make_management_files (module, text);
1311
1312    return (true);
1313  }
1314
1315  /**
1316     Effective checkout of a package or a project
1317   */
1318  bool really_checkout (const cmt_string& offset,
1319                        const cmt_string& product,
1320                        const cmt_string& version,
1321                        const cmt_string& tag,
1322                        const cmt_string& module,
1323                        const cmt_string& basedir,
1324                        bool at_head)
1325  {
1326    cmt_string dir = basedir;
1327    cmt_string out;
1328   
1329    cout << "# ================= working on " << structure_info << " " << product
1330         << " version " << version;
1331
1332    if (at_head) cout << " (At head) ";
1333
1334    cmt_string full_offset;
1335
1336    full_offset = m_cvs_offset;
1337    full_offset += offset;
1338
1339    cmt_string echo_ppath;
1340       
1341    if (offset != "")
1342      {
1343        echo_ppath = " path ";
1344        echo_ppath += offset;
1345      }
1346   
1347    cout << echo_ppath << " in " << dir << endl;
1348
1349    if (do_need_version ())
1350      {
1351        // Move back to the product name.
1352        CmtSystem::dirname (dir, dir);
1353      }
1354
1355    if (!mkdir (dir)) return (false);
1356       
1357    if (structure_info == "package")
1358      {
1359        really_checkout_package_contents (offset,
1360                                          product,
1361                                          version,
1362                                          module,
1363                                          basedir,
1364                                          at_head,
1365                                          dir);
1366      }
1367    else if (structure_info == "project")
1368      {
1369        really_checkout_project_contents (offset,
1370                                          product,
1371                                          version,
1372                                          tag,
1373                                          module,
1374                                          basedir,
1375                                          at_head,
1376                                          dir);
1377      }
1378
1379    return (true);
1380  }
1381
1382  /**
1383     Find the most appropriate effective version directory corresponding to the
1384     specified version expression.
1385     The specified expression may contain wildcards (in the file manager sense). This
1386     FME is first converted into a RE then a directory search is performed.
1387     An empty string is returned if no match is found.
1388   */
1389  cmt_string find_matching_version (const cmt_string& expression)
1390  {
1391    cmt_string result;
1392
1393    //
1394    // Here expression takes the form
1395    //   <some path>/<expression with wild-card>
1396    //
1397
1398    cmt_string dir;
1399    CmtSystem::dirname (expression, dir);
1400    dir += CmtSystem::file_separator ();
1401   
1402    cmt_string version;
1403    CmtSystem::basename (expression, version);
1404
1405    if (version.find ("*") == cmt_string::npos)
1406      {
1407        // there is no wildcarding here. A simple test is enough.
1408        if (CmtSystem::test_directory (expression))
1409          {
1410            result = version;
1411          }
1412      }
1413    else
1414      {
1415        version.replace ("*", ".*");
1416       
1417        cmt_regexp exp (version);
1418       
1419        CmtSystem::cmt_string_vector list;
1420       
1421        CmtSystem::scan_dir (dir, exp, list);
1422       
1423        if (list.size () > 0)
1424          {
1425            result = list[0];
1426           
1427            CmtSystem::basename (result, result);
1428          }
1429      }
1430   
1431    return (result);
1432  }
1433 
1434  /**
1435   *   We provide a path to a requirements file. From it we read the use
1436   *  statements, and we try to checkout the corresponding packages.
1437   *
1438   *   A boolean return tells if any recursion occurred.
1439   */
1440  bool checkout_from_requirements (const cmt_string& requirements_path)
1441  {
1442    static cmt_regexp expression ("^[ \t]*use[ \t]");
1443
1444    cmt_string text;
1445
1446    text.read (requirements_path);
1447
1448    RecursivePass1 p1;
1449    p1.run (text, expression);
1450
1451    bool result = (p1.result () != "");
1452    RecursivePass2 p2 (*this);
1453    p2.run (p1.result ());
1454
1455    return (result);
1456  }
1457
1458  /**
1459   *   We provide a path to a project file. From it we read the use
1460   *  statements, and we try to checkout the corresponding projects.
1461   */
1462  void checkout_from_project_file (const cmt_string& file_name)
1463  {
1464    static cmt_regexp expression ("^[ \t]*use[ \t]");
1465
1466    cmt_string text;
1467
1468    text.read (file_name);
1469
1470    CvsImplementation& me = *this;
1471    CvsImplementation saved;
1472    saved = me;
1473    cmt_string here = CmtSystem::pwd ();
1474
1475    RecursivePass3 p3;
1476    p3.run (text, expression);
1477
1478    RecursivePass4 p4 (*this);
1479    p4.run (p3.result ());
1480
1481    Grep grep;
1482
1483    grep.run (text, "container");
1484    cmt_string container = grep.result ();
1485
1486    if (container != "")
1487      {
1488        static cmt_regexp container_expression ("^[ \t]*container[ \t]");
1489
1490        add_cmtpath (here);
1491
1492        cout << "  # --> now getting project packages from the " << container << " " << here << endl;
1493
1494        CmtSystem::cd (here);
1495
1496        RecursivePass1 p1;
1497        p1.run (text, container_expression);
1498
1499        RecursivePass2 p2 (*this);
1500
1501        m_home_dir = CmtSystem::pwd ();
1502        p2.run (p1.result ());
1503      }
1504
1505    CmtSystem::cd (here);
1506    me = saved;
1507  }
1508
1509  /**
1510     Check whether a given directory structure matches an expected product structure
1511     given by the structure info obtained from the most recent request to the CVS pluggin
1512   */
1513  bool check_structure (const cmt_string& dir)
1514  {
1515    bool result = false;
1516
1517    if (!CmtSystem::test_directory (dir))
1518      {
1519        return (false);
1520      }
1521
1522    if (structure_info == "package")
1523      {
1524        // Check if it is a true CMT package.
1525       
1526        cmt_string file_name;
1527       
1528        file_name = dir;
1529        file_name += CmtSystem::file_separator ();
1530        file_name += "cmt";
1531        file_name += CmtSystem::file_separator ();
1532        file_name += "requirements";
1533       
1534        if (CmtSystem::test_file (file_name))
1535          {
1536            result = true;
1537          }
1538        else
1539          {
1540            file_name = dir;
1541            file_name += CmtSystem::file_separator ();
1542            file_name += "mgr";
1543            file_name += CmtSystem::file_separator ();
1544            file_name += "requirements";
1545           
1546            if (CmtSystem::test_file (file_name))
1547              {
1548                result = true;
1549              }
1550          }
1551      }
1552    else if (structure_info == "project")
1553      {
1554        cmt_string file_name;
1555       
1556        file_name = dir;
1557        file_name += CmtSystem::file_separator ();
1558        file_name += "cmt";
1559        file_name += CmtSystem::file_separator ();
1560        file_name += "project.cmt";
1561       
1562        if (CmtSystem::test_file (file_name))
1563          {
1564            result = true;
1565          }
1566      }
1567
1568    return (result);
1569  }
1570
1571  /**
1572     Internal call from the initial do_checkout or from recursive passes
1573     Prepare the directory structure for the checkout
1574     Do the checkout
1575     Check if everything was done properly, if a package or a project has been created
1576     If needed recurse from the requirements or project file
1577
1578     For projects there may be two different specifications for
1579     - the version directory
1580     - the CVS tag
1581   */
1582  void do_checkout_phase2 (const cmt_string& offset,
1583                           const cmt_string& product,
1584                           const cmt_string& specified_version,
1585                           const cmt_string& tag)
1586  {
1587    if (m_verbose)
1588      {
1589        cout << "#CMT> do_checkout_phase2> offset=" << offset
1590             << " " << structure_info << "=" << product
1591             << " specified_version=" << specified_version
1592             << " tag=" << tag
1593             << " pwd=" << CmtSystem::pwd ()
1594             << endl;
1595      }
1596   
1597    cmt_string version = specified_version;
1598    cmt_string empty;
1599    cmt_string full_offset;
1600
1601    full_offset = m_cvs_offset;
1602    full_offset += offset;
1603   
1604    cmt_string echo_ppath;
1605   
1606    if (offset != "")
1607      {
1608        echo_ppath = " path ";
1609        echo_ppath += offset;
1610      }
1611   
1612    if (version == "")
1613      {
1614        cout << "# ================= No version specified for " << structure_info << " " << product << endl;
1615        return;
1616      }
1617   
1618    //
1619    //  First make an attempt to locate the specified version of
1620    //  this product "as-it-is" in the work area.
1621    //   Since 'version' may contain wild-card, it's likely that
1622    //  we should not simply use CmtSystem::test_directory but
1623    //  use the wild-card search.
1624    //
1625   
1626    cmt_string dir;
1627   
1628    dir = build_version_directory (offset, product, version);
1629   
1630    bool recursive = m_recursive;
1631   
1632    cmt_string effective_version = find_matching_version (dir);
1633   
1634    cmt_string module;
1635    get_module (full_offset, product, module);
1636
1637    cmt_string cvs_tag = (tag != "") ? tag : version;
1638    bool at_head = false;
1639
1640    if (effective_version != "")
1641      {
1642        version = effective_version;
1643      }
1644    else
1645      {
1646        //
1647        // get_version attempts to find the most appropriate version
1648        // tag matching the specification FROM the repository. However,
1649        // we should take into account situations where some versions have
1650        // already been checked out, in which case they might be sufficient
1651        // (or preferred?)
1652        //
1653       
1654        if (cvs_tag.find ("*") != cmt_string::npos)
1655          {
1656            cout << "# ================= " << structure_info << " " << product
1657                 << " version " << cvs_tag << echo_ppath
1658                 << " has wild cards and will not be considered." << endl;
1659            return;
1660          }
1661       
1662        if (!get_version (full_offset, product, cvs_tag, module,
1663                          cvs_tag, at_head))
1664          {
1665            return;
1666          }
1667       
1668        if (m_head)
1669          {
1670            m_head = false;
1671           
1672            at_head = true;
1673          }
1674        else
1675          {
1676            at_head = false;
1677          }
1678       
1679        //
1680        // Make a second try after having selected a CVS tag from all the
1681        // available tags compatible with the specified version
1682        //
1683
1684        if (tag == "")
1685          {
1686            // If tag was not specified, then the version directory has to match the CVS tag
1687            // Otherwise the original version specification is kept for the directory.
1688
1689            version = cvs_tag;
1690            dir = build_version_directory (offset, product, version);
1691          }
1692      }
1693   
1694
1695    if (check_structure (dir))
1696      {
1697        cout << "# ================= " << structure_info << " " << product
1698             << " version " << version << echo_ppath
1699             << " already installed." << endl;
1700       
1701        recursive = false;
1702      }
1703    else
1704      {
1705        //
1706        // Now we can say that we have to perform the real checkout.
1707        //
1708       
1709        if (!really_checkout (offset, product, version, cvs_tag, module, dir, at_head))
1710          {
1711            cout << "# bad return from really_checkout_product" << endl;
1712            return;
1713          }
1714      }
1715
1716    //
1717    //  Now reach the newly checked out product.
1718    //
1719   
1720    if (m_simulation)
1721      {
1722        cout << "#CMT> " << structure_info << " directory not really created " << dir << endl;
1723      }
1724    else if (structure_info == "package")
1725      {
1726        if (!CmtSystem::cd (dir))
1727          {
1728            cout << "#CMT> Package directory not created " << dir << endl;
1729            return;
1730          }
1731       
1732        // Check if it is a true CMT package.
1733       
1734        cmt_string file_name;
1735       
1736        file_name = "cmt";
1737        file_name += CmtSystem::file_separator ();
1738        file_name += "requirements";
1739       
1740        if (CmtSystem::test_file (file_name))
1741          {
1742            dir += CmtSystem::file_separator ();
1743            dir += "cmt";
1744            CmtSystem::cd ("cmt");
1745           
1746            if (!do_need_version ())
1747              {
1748                cmt_string text = version;
1749                text += "\n";
1750                text.write ("version.cmt");
1751              }
1752          }
1753        else
1754          {
1755            file_name = "mgr";
1756            file_name += CmtSystem::file_separator ();
1757            file_name += "requirements";
1758           
1759            if (CmtSystem::test_file (file_name))
1760              {
1761                dir += CmtSystem::file_separator ();
1762                dir += "mgr";
1763                CmtSystem::cd ("mgr");
1764              }
1765            else
1766              {
1767                cout << "# " << product << " not a CMT package" << endl;
1768                return;
1769              }
1770          }
1771       
1772        if (recursive)
1773          {
1774            cmt_string here = CmtSystem::pwd ();
1775
1776            bool did_recurse = checkout_from_requirements ("requirements");
1777
1778            CmtSystem::cd (here);
1779
1780            if (did_recurse) execute ("cmt -quiet broadcast cmt -quiet config");
1781          }
1782      }
1783    else if (structure_info == "project")
1784      {
1785       
1786        if (m_verbose)
1787          {
1788            cout << "#CMT> dir=" << dir << endl;
1789          }
1790       
1791        if (!CmtSystem::cd (dir))
1792          {
1793            cout << "#CMT> Project directory not created " << dir << endl;
1794            return;
1795          }
1796       
1797        cmt_string file_name;
1798       
1799        file_name = "cmt";
1800        file_name += CmtSystem::file_separator ();
1801        file_name += "project.cmt";
1802       
1803        if (!CmtSystem::test_file (file_name))
1804          {
1805            cout << "# " << product << " not a CMT project" << endl;
1806            return;
1807          }
1808       
1809        if (recursive)
1810          {
1811            checkout_from_project_file (file_name);
1812          }
1813
1814        cout << "# ================= Project " << product << " completed" << endl;
1815
1816      }
1817  }
1818 
1819  /**
1820     Top level of the checkout operation, initiated from the command line arguments
1821
1822     Construct an history of the checkouts to avoid duplicating the
1823     checkouts during the recursivity
1824
1825     Eventually
1826     o perform the cmt config for packages.
1827
1828   */
1829  void do_checkout_phase1 (const cmt_string& module, 
1830                           const cmt_string& version_dir, 
1831                           const cmt_string& version_tag)
1832  {
1833    add_cmtpath (m_home_dir);
1834
1835    History& h = History::instance ();
1836
1837    h.clear ();
1838
1839    if (module == "")
1840      {
1841        if (m_verbose)
1842          {
1843            cout << "#CMT> Missing module name" << endl;
1844          }
1845        return;
1846      }
1847
1848    cmt_string offset;
1849    cmt_string product;
1850    cmt_string version;
1851    cmt_string tag;
1852   
1853    {
1854      cmt_string m;
1855      m = m_cvs_offset;
1856      m += module;
1857       
1858      get_cvs_infos_with_offset (m);
1859       
1860      if (error_info != "")
1861        {
1862          cout << error_info << endl;
1863          return;
1864        }
1865    }
1866
1867    if (version_tag == "")
1868      {
1869        Cut cut (0);
1870       
1871        if (tags_top_info != "") tag = tags_top_info;
1872        else tag = tags_info;
1873       
1874        cut.run (tag);
1875       
1876        tag = cut.result ();
1877      }
1878    else
1879      {
1880        tag = version_tag;
1881      }
1882
1883    version = (version_dir == "") ? tag : version_dir;
1884
1885    CmtSystem::dirname (module, offset);
1886    CmtSystem::basename (module, product);
1887   
1888    cmt_string top_dir;
1889       
1890    top_dir = m_home_dir;
1891    top_dir += CmtSystem::file_separator ();
1892    top_dir += m_checkout_dir;
1893    top_dir += CmtSystem::file_separator ();
1894    top_dir += offset;
1895    top_dir += CmtSystem::file_separator ();
1896    top_dir += product;
1897    top_dir += CmtSystem::file_separator ();
1898    top_dir += version;
1899
1900    CmtSystem::reduce_file_separators (top_dir);
1901
1902    if (m_verbose)
1903      {
1904        cout << "#CMT> about to checkout " << structure_info
1905             << " " << product << " version " << version << " into " << top_dir << endl;
1906      }
1907
1908    static const cmt_string empty;
1909    do_checkout_phase2 (offset, product, version, tag);
1910
1911    if (m_simulation) return;
1912
1913    if (!CmtSystem::cd (top_dir)) return;
1914
1915    if (structure_info == "project")
1916      {
1917        cmt_string file_name;
1918   
1919        file_name = "cmt";
1920        file_name += CmtSystem::file_separator ();
1921        file_name += "project.cmt";
1922       
1923        if (!CmtSystem::test_file (file_name))
1924          {
1925            cout << "# " << product << " was not properly checked out and is missing its cmt/project.cmt file" << endl;
1926            return;
1927          }
1928      }
1929    else
1930      {
1931        cmt_string file_name;
1932   
1933        file_name = "cmt";
1934        file_name += CmtSystem::file_separator ();
1935        file_name += "requirements";
1936       
1937        if (CmtSystem::test_file (file_name))
1938          {
1939            top_dir += CmtSystem::file_separator ();
1940            top_dir += "cmt";
1941            CmtSystem::cd ("cmt");
1942          }
1943        else
1944          {
1945            file_name = "mgr";
1946            file_name += CmtSystem::file_separator ();
1947            file_name += "requirements";
1948           
1949            if (CmtSystem::test_file (file_name))
1950              {
1951                top_dir += CmtSystem::file_separator ();
1952                top_dir += "mgr";
1953                CmtSystem::cd ("mgr");
1954              }
1955            else
1956              {
1957                cout << "# " << product << " was not properly checked out and is missing its cmt/requirements file" << endl;
1958                return;
1959              }
1960          }
1961       
1962        if (m_verbose)
1963          {
1964            cout << "#CMT> product " << product << " has been checked out" << endl;
1965          }
1966       
1967        if (!m_recursive)
1968          {
1969            execute ("cmt -quiet config");
1970          }
1971      }
1972  }
1973
1974  void help ()
1975  {
1976    cout << "> cd <some work area>" << endl;
1977    cout << "> cmt checkout [modifier ...] <package|project>" << endl;
1978    cout << "" << endl;
1979    cout << "   modifier :" << endl;
1980    cout << "   -l        Do not process used packages (default)." << endl;
1981    cout << "   -R        Process used products recursively." << endl;
1982    cout << "   -r rev    Check out version tag. (is sticky)" << endl;
1983    cout << "   -vd dir   Use this version directory instead of CVS tag." << endl;
1984    cout << "   -d dir    Check out into dir instead of module name." << endl;
1985    cout << "   -o offset Offset in the CVS repository" << endl;
1986    cout << "   -requirements <requirements file path>  Check out packages referenced in this requirements file" << endl;
1987    cout << "   -n        simulation mode on" << endl;
1988    cout << "   -v        verbose mode on" << endl;
1989    cout << "   --help    print this help" << endl;
1990    cout << "" << endl;
1991    cout << "> cmt cvstags <package|project>" << endl;
1992    cout << "> cmt cvssubpackages <directory>" << endl;
1993    cout << "> cmt cvssubprojects <directory>" << endl;
1994    cout << "" << endl;
1995  }
1996
1997  /**
1998     Implementation of the cmt cvstags
1999     Get the CVS tags of a module
2000   */
2001  void tags (const CmtSystem::cmt_string_vector& arguments)
2002  {
2003    if (arguments.size () < 1)
2004      {
2005        help ();
2006        return;
2007      }
2008   
2009    if (CmtSystem::getenv ("CVSROOT") == "")
2010      {
2011        cout << "# Please set CVSROOT first !" << endl;
2012        return;
2013      }
2014   
2015    m_cvs_offset = CmtSystem::getenv ("CMTCVSOFFSET");
2016    if (m_cvs_offset != "") 
2017      {
2018        m_cvs_offset += "/";
2019        m_cvs_offset.replace_all ("//", "/");
2020      }
2021   
2022    bool all = false;
2023   
2024    for (int arg = 0; arg < arguments.size (); arg++)
2025      {
2026        const cmt_string& option = arguments[arg];
2027       
2028        if (option == "-all")
2029          {
2030            all = true;
2031          }
2032        else
2033          {
2034            get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), option);
2035           
2036            if (error_info != "")
2037              {
2038                cout << error_info << endl;
2039              }
2040            else
2041              {
2042                cmt_string tags;
2043               
2044                if (all)
2045                  {
2046                    tags = cvsversions_top_info;
2047                    tags += " ";
2048                    tags += cvsversions_info;
2049                  }
2050                else
2051                  {
2052                    tags = tags_top_info;
2053                    tags += " ";
2054                    tags += tags_info;
2055                  }
2056               
2057                CmtSystem::cmt_string_vector v;
2058               
2059                CmtSystem::split (tags, " \t", v);
2060                for (int i = 0; i < v.size (); i++)
2061                  {
2062                    const cmt_string& s = v[i];
2063                    cout << s << endl;
2064                  }
2065              }
2066          }
2067      }
2068  }
2069
2070  /**
2071     Implementation of the cmt cvsbranches
2072     Get the subdirs of a module that are not subackages
2073   */
2074  void branches (const cmt_string& module)
2075  {
2076    cmt_string out;
2077   
2078    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), module);
2079   
2080    if (error_info != "")
2081      {
2082        cout << error_info << endl;
2083      }
2084    else
2085      {
2086        cout << branches_info << endl;
2087      }
2088  }
2089
2090  /**
2091     Implementation of the cmt cvssubpackages
2092     Get the subdirs of a module that ARE CMT subpackages
2093   */
2094  void subpackages (const cmt_string& module)
2095  {
2096    cmt_string out;
2097   
2098    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), (module == "") ? "." : module);
2099   
2100    if (error_info != "")
2101      {
2102        cout << error_info << endl;
2103      }
2104    else
2105      {
2106        cout << subpackages_info << endl;
2107      }
2108  }
2109 
2110  /**
2111     Implementation of the cmt cvssubrojects
2112     Get the subdirs of a module that ARE CMT projects
2113   */
2114  void subprojects (const cmt_string& module)
2115  {
2116    cmt_string out;
2117   
2118    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), (module == "") ? "." : module);
2119   
2120    if (error_info != "")
2121      {
2122        cout << error_info << endl;
2123      }
2124    else
2125      {
2126        cout << subprojects_info << endl;
2127      }
2128  }
2129
2130  /**
2131     Implementation of the cmt checkout
2132     Parse the arguments
2133     Then call do_checkout for each argument
2134   */
2135  void checkout (const CmtSystem::cmt_string_vector& arguments)
2136  {
2137    if (arguments.size () < 1)
2138      {
2139        help ();
2140        return;
2141      }
2142   
2143    if (CmtSystem::getenv ("CVSROOT") == "")
2144      {
2145        cout << "# Please set CVSROOT first !" << endl;
2146        return;
2147      }
2148   
2149    m_home_dir = CmtSystem::pwd ();
2150    m_checkout_dir = "";
2151    m_version_dir = "";
2152    m_cvs_offset = "";
2153
2154    cmt_string module;
2155   
2156    m_recursive = false;
2157   
2158    bool need_version_tag = false;
2159    cmt_string version_tag;
2160   
2161    bool need_checkout_dir = false;
2162    bool need_cvs_offset = false;
2163    bool need_requirements_file = false;
2164    bool need_version_dir = false;
2165   
2166    m_simulation = false;
2167    //m_verbose = true;
2168    m_verbose = false;
2169   
2170    m_head = true;
2171   
2172    m_cvs_offset = CmtSystem::getenv ("CMTCVSOFFSET");
2173    if (m_cvs_offset != "") 
2174      {
2175        m_cvs_offset += "/";
2176        m_cvs_offset.replace_all ("//", "/");
2177      }
2178   
2179    for (int arg = 0; arg < arguments.size (); arg++)
2180      {
2181        const cmt_string& option = arguments[arg];
2182       
2183        if (need_version_tag)
2184          {
2185            need_version_tag = false;
2186           
2187            if (option == "HEAD")
2188              {
2189                m_head = true;
2190              }
2191            else
2192              {
2193                version_tag = option;
2194              }
2195          }
2196        else if (need_checkout_dir)
2197          {
2198            need_checkout_dir = false;
2199            m_checkout_dir = option;
2200          }
2201        else if (need_version_dir)
2202          {
2203            need_version_dir = false;
2204            m_version_dir = option;
2205          }
2206        else if (need_cvs_offset)
2207          {
2208            need_cvs_offset = false;
2209            m_cvs_offset = option;
2210            m_cvs_offset += '/';
2211            m_cvs_offset.replace_all ("//", "/");
2212          }
2213        else if (need_requirements_file)
2214          {
2215            need_requirements_file = false;
2216            m_head = false;
2217            checkout_from_requirements (option);
2218          }
2219        else
2220          {
2221            if (option == "-R")
2222              {
2223                m_recursive = true;
2224              }
2225            else if (option == "-l")
2226              {
2227                m_recursive = false;
2228              }
2229            else if (option == "-r")
2230              {
2231                need_version_tag = true;
2232                m_head = false;
2233              }
2234            else if (option == "-d")
2235              {
2236                need_checkout_dir = true;
2237              }
2238            else if (option == "-o")
2239              {
2240                need_cvs_offset = true;
2241              }
2242            else if (option == "-n")
2243              {
2244                m_simulation = true;
2245              }
2246            else if (option == "-v")
2247              {
2248                m_verbose = true;
2249              }
2250            else if (option == "-vd")
2251              {
2252                need_version_dir = true;
2253              }
2254            else if (option == "-requirements")
2255              {
2256                need_requirements_file = true;
2257              }
2258            else if (option == "--help")
2259              {
2260                help ();
2261                return;
2262              }
2263            else if (option[0] == '-')
2264              {
2265                help ();
2266                return;
2267              }
2268            else
2269              {
2270                do_checkout_phase1 (option, m_version_dir, version_tag);
2271              }
2272          }
2273      }
2274  }
2275
2276private:
2277
2278  bool m_recursive;
2279  bool m_head;
2280  bool m_verbose;
2281  bool m_simulation;
2282
2283  cmt_string m_home_dir;
2284  cmt_string m_checkout_dir;
2285  cmt_string m_version_dir;
2286  cmt_string m_cvs_offset;
2287
2288  cmt_string m_protocol_level;
2289  cmt_string m_last_module;
2290  cmt_string m_last_cvs_infos;
2291  cmt_string structure_info;
2292  cmt_string error_info;
2293  cmt_string tags_top_info;
2294  cmt_string tags_info;
2295  cmt_string cvsversions_top_info;
2296  cmt_string cvsversions_info;
2297  cmt_string branches_info;
2298  cmt_string subpackages_info;
2299  cmt_string subprojects_info;
2300};
2301
2302//--------------------------------------------------------------------
2303
2304void Grep::begin ()
2305{
2306  m_result = "";
2307}
2308
2309void Grep::filter (const cmt_string& line)
2310{
2311    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "Grep::filter" << endl;
2312
2313  if (m_result != "") m_result += " ";
2314  m_result += line;
2315}
2316
2317const cmt_string& Grep::result () const
2318{
2319  return (m_result);
2320}
2321
2322//--------------------------------------------------------------------
2323
2324Cut::Cut (int field)
2325{
2326  m_field = field;
2327}
2328
2329void Cut::begin ()
2330{
2331    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "Cut::begin" << endl;
2332  m_result = "";
2333}
2334
2335void Cut::filter (const cmt_string& line)
2336{
2337    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "Cut::filter" << endl;
2338
2339  static CmtSystem::cmt_string_vector words;
2340 
2341  CmtSystem::split (line, " \t", words);
2342 
2343  if (words.size () <= m_field) return;
2344 
2345  if (m_result != "") m_result += " ";
2346  m_result += words[m_field];
2347}
2348
2349const cmt_string& Cut::result () const
2350{
2351  return (m_result);
2352}
2353
2354//--------------------------------------------------------------------
2355//--------------------------------------------------------------------
2356
2357History& History::instance ()
2358{
2359  static History h;
2360  return (h);
2361}
2362
2363void History::clear ()
2364{
2365  m_installed = "";
2366}
2367
2368void History::install (const cmt_string& line)
2369{
2370  m_installed += "|";
2371  m_installed += line;
2372  m_installed += "|";
2373}
2374
2375bool History::is_installed (const cmt_string& line)
2376{
2377  if (m_installed.find (line) != cmt_string::npos)
2378    {
2379      return (true);
2380    }
2381 
2382  return (false);
2383}
2384
2385History::History ()
2386{
2387}
2388
2389
2390//--------------------------------------------------------------------
2391
2392void RecursivePass1::begin ()
2393{
2394  m_first = true;
2395  m_result = "";
2396}
2397
2398void RecursivePass1::filter (const cmt_string& line)
2399{
2400    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "RecursivePass1::filter> "
2401    //                                          << "line=[" << line << "]" << endl;
2402
2403  if (line.find ("use CMT") != cmt_string::npos) return;
2404  if (line.find ("use cmt") != cmt_string::npos) return;
2405 
2406  History& h = History::instance ();
2407 
2408  if (h.is_installed (line)) return;
2409 
2410  CmtSystem::cmt_string_vector words;
2411 
2412  CmtSystem::split (line, " \t", words);
2413
2414  enum
2415  {
2416    need_package,
2417    need_version,
2418    need_path,
2419    no_need
2420  } state = need_package;
2421
2422  cmt_string package;
2423  cmt_string version;
2424  cmt_string path;
2425
2426  for (int i = 1; i < words.size (); i++)
2427    {
2428      const cmt_string& s = words[i];
2429
2430      if (s[0] == '-') continue;
2431
2432      switch (state)
2433        {
2434          case need_package:
2435            package = s;
2436            state = need_version;
2437            break;
2438          case need_version:
2439            version = s;
2440            state = need_path;
2441            break;
2442          case need_path:
2443            path = s;
2444            state = no_need;
2445            break;
2446        }
2447    }
2448 
2449  if (version.find ("*") != cmt_string::npos)
2450    {
2451        /*
2452      cout << "# ================= Package " << package
2453           << " version " << version << " " << path
2454           << " has wild cards and will not be considered." << endl;
2455        */
2456      return;
2457    }
2458
2459  /**
2460   *  At the first pass, we simply accumulate the not-yet handled
2461   *  use statements.
2462   */
2463 
2464  m_result += line;
2465  m_result += "\n";
2466 
2467  if (m_first)
2468    {
2469      m_first = false;
2470      cout << "  # --> now propagate cmt checkout to :" << endl;
2471    }
2472 
2473  cout << "  #     " << package << " " << version << " " << path << endl;
2474}
2475
2476const cmt_string& RecursivePass1::result () const
2477{
2478  return (m_result);
2479}
2480
2481//--------------------------------------------------------------------
2482
2483RecursivePass2::RecursivePass2 (CvsImplementation& cvs) : m_cvs (cvs)
2484{
2485}
2486
2487void RecursivePass2::begin ()
2488{
2489}
2490
2491void RecursivePass2::filter (const cmt_string& line)
2492{
2493    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "RecursivePass2::filter> "
2494    //                                          << "line=[" << line << "]" << endl;
2495
2496  /**
2497   *   At the second pass, the lines are really handled. Thus
2498   *   the lines are stored into m_installed so as to avoid
2499   *   later on re-installation.
2500   */
2501 
2502  History& h = History::instance ();
2503 
2504  if (h.is_installed (line)) return;
2505 
2506  h.install (line);
2507 
2508  CmtSystem::cmt_string_vector words;
2509 
2510  CmtSystem::split (line, " \t", words);
2511
2512  enum
2513  {
2514    need_package,
2515    need_version,
2516    need_path,
2517    no_need
2518  } state = need_package;
2519
2520  cmt_string package;
2521  cmt_string version;
2522  cmt_string path;
2523
2524  for (int i = 1; i < words.size (); i++)
2525    {
2526      const cmt_string& s = words[i];
2527
2528      if (s[0] == '-') continue;
2529
2530      switch (state)
2531        {
2532          case need_package:
2533            package = s;
2534            state = need_version;
2535            break;
2536          case need_version:
2537            version = s;
2538            state = need_path;
2539            break;
2540          case need_path:
2541            path = s;
2542            state = no_need;
2543            break;
2544        }
2545    }
2546
2547  if (version.find ("*") != cmt_string::npos)
2548    {
2549        /*
2550      cout << "# ================= Package " << package
2551           << " version " << version << " " << path
2552           << " has wild cards and will not be considered." << endl;
2553        */
2554    }
2555  else
2556    {
2557      static const cmt_string empty;
2558      m_cvs.do_checkout_phase2 (path, package, version, empty);
2559    }
2560}
2561
2562//--------------------------------------------------------------------
2563
2564void RecursivePass3::begin ()
2565{
2566  m_first = true;
2567  m_result = "";
2568}
2569
2570void RecursivePass3::filter (const cmt_string& line)
2571{
2572  History& h = History::instance ();
2573 
2574  if (h.is_installed (line)) return;
2575 
2576  CmtSystem::cmt_string_vector words;
2577 
2578  CmtSystem::split (line, " \t", words);
2579
2580  enum
2581  {
2582    need_project,
2583    need_version,
2584    need_tag,
2585    no_need
2586  } state = need_project;
2587
2588  cmt_string project;
2589  cmt_string version;
2590  cmt_string tag;
2591
2592  for (int i = 1; i < words.size (); i++)
2593    {
2594      const cmt_string& s = words[i];
2595
2596      switch (state)
2597        {
2598          case need_project:
2599            project = s;
2600            state = need_version;
2601            break;
2602          case need_version:
2603            version = s;
2604            state = need_tag;
2605            break;
2606          case need_tag:
2607            tag = s;
2608            state = no_need;
2609            break;
2610        }
2611    }
2612 
2613  if (version.find ("*") != cmt_string::npos)
2614    {
2615        /*
2616      cout << "# ================= Project " << project
2617           << " version " << version << " " << path
2618           << " has wild cards and will not be considered." << endl;
2619        */
2620      return;
2621    }
2622
2623  /**
2624   *  At the first pass, we simply accumulate the not-yet handled
2625   *  use statements.
2626   */
2627 
2628  m_result += line;
2629  m_result += "\n";
2630 
2631  if (m_first)
2632    {
2633      m_first = false;
2634      cout << "  # --> now propagate cmt checkout to :" << endl;
2635    }
2636 
2637  cout << "  #     " << project << " " << version << " " << tag << endl;
2638}
2639
2640const cmt_string& RecursivePass3::result () const
2641{
2642  return (m_result);
2643}
2644
2645//--------------------------------------------------------------------
2646
2647RecursivePass4::RecursivePass4 (CvsImplementation& cvs) : m_cvs (cvs)
2648{
2649}
2650
2651void RecursivePass4::begin ()
2652{
2653}
2654
2655void RecursivePass4::filter (const cmt_string& line)
2656{
2657    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "RecursivePass4::filter> "
2658    //                                          << "line=[" << line << "]" << endl;
2659
2660  /**
2661   *   At the second pass, the lines are really handled. Thus
2662   *   the lines are stored into m_installed so as to avoid
2663   *   later on re-installation.
2664   */
2665 
2666  History& h = History::instance ();
2667 
2668  if (h.is_installed (line)) return;
2669 
2670  h.install (line);
2671 
2672  CmtSystem::cmt_string_vector words;
2673 
2674  CmtSystem::split (line, " \t", words);
2675
2676  enum
2677  {
2678    need_project,
2679    need_version,
2680    need_tag,
2681    no_need
2682  } state = need_project;
2683
2684  cmt_string project;
2685  cmt_string version;
2686  cmt_string tag;
2687
2688  for (int i = 1; i < words.size (); i++)
2689    {
2690      const cmt_string& s = words[i];
2691
2692      switch (state)
2693        {
2694          case need_project:
2695            project = s;
2696            state = need_version;
2697            break;
2698          case need_version:
2699            version = s;
2700            state = need_tag;
2701            break;
2702          case need_tag:
2703            tag = s;
2704            state = no_need;
2705            break;
2706        }
2707    }
2708
2709  if (version.find ("*") != cmt_string::npos)
2710    {
2711        /*
2712      cout << "# ================= Project " << project
2713           << " version " << version
2714           << " has wild cards and will not be considered." << endl;
2715        */
2716    }
2717  else
2718    {
2719      static const cmt_string empty;
2720      m_cvs.do_checkout_phase2 (empty, project, version, tag);
2721    }
2722}
2723
2724//--------------------------------------------------------------------
2725
2726void Cvs::tags (const CmtSystem::cmt_string_vector& arguments)
2727{
2728  CvsImplementation cvs;
2729
2730  cvs.tags (arguments);
2731}
2732
2733void Cvs::branches (const cmt_string& module)
2734{
2735  CvsImplementation cvs;
2736
2737  cvs.branches (module);
2738}
2739
2740void Cvs::subpackages (const cmt_string& module)
2741{
2742  CvsImplementation cvs;
2743
2744  cvs.subpackages (module);
2745}
2746
2747void Cvs::subprojects (const cmt_string& module)
2748{
2749  CvsImplementation cvs;
2750
2751  cvs.subprojects (module);
2752}
2753
2754void Cvs::checkout (const CmtSystem::cmt_string_vector& arguments)
2755{
2756  CvsImplementation cvs;
2757
2758  cvs.checkout (arguments);
2759}
2760
Note: See TracBrowser for help on using the repository browser.