# -*- python -*-
#                           Package   : omniidl
# __init__.py               Created on: 1999/11/3
#			    Author    : David Scott (djs)
#
#    Copyright (C) 1999 AT&T Laboratories Cambridge
#
#  This file is part of omniidl.
#
#  omniidl is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#  02111-1307, USA.
#
# Description:
#
#   Entrypoint to header generation code

# $Id: __init__.py,v 1.12.2.8 2000/08/10 10:38:23 sll Exp $
# $Log: __init__.py,v $
# Revision 1.12.2.8  2000/08/10 10:38:23  sll
# Support new pragma hh in the cxx omniidl backend.  Added CPP macro guards
# to stub header to preserve the value of USE_core_stub_in_nt_dll and
# USE_dyn_stub_in_nt_dll.
#
# Revision 1.12.2.7  2000/06/26 16:23:57  djs
# Better handling of #include'd files (via new commandline options)
# Refactoring of configuration state mechanism.
#
# Revision 1.12.2.6  2000/06/05 13:03:54  djs
# Removed union member name clash (x & pd_x, pd__default, pd__d)
# Removed name clash when a sequence is called "pd_seq"
# Nested union within union fix
# Actually generates BOA non-flattened tie templates
#
# Revision 1.12.2.5  2000/05/31 18:02:56  djs
# Better output indenting (and preprocessor directives now correctly output at
# the beginning of lines)
#
# Revision 1.12.2.4  2000/04/26 18:22:27  djs
# Rewrote type mapping code (now in types.py)
# Rewrote identifier handling code (now in id.py)
#
# Revision 1.12.2.3  2000/03/21 10:58:49  djs
# Forgot to make fragment mode use LazyStream instead of Stream
#
# Revision 1.12.2.2  2000/03/20 11:50:18  djs
# Removed excess buffering- output templates have code attached which is
# lazily evaluated when required.
#
# Revision 1.12.2.1  2000/02/14 18:34:55  dpg1
# New omniidl merged in.
#
# Revision 1.12  2000/01/20 18:26:13  djs
# Tie template output order problem
#
# Revision 1.11  2000/01/19 11:23:27  djs
# Moved most C++ code to template file
#
# Revision 1.10  2000/01/17 17:00:21  djs
# Runs tcParser #ifdefs for bounded strings
#
# Revision 1.9  2000/01/11 11:34:27  djs
# Added support for fragment generation (-F) mode
#
# Revision 1.8  2000/01/10 17:18:14  djs
# Removed redundant code.
#
# Revision 1.7  1999/12/24 18:14:29  djs
# Fixed handling of #include'd .idl files
#
# Revision 1.6  1999/12/13 15:40:26  djs
# Added generation of "flattened" tie templates
#
# Revision 1.5  1999/12/01 17:03:37  djs
# Added new modules
#
# Revision 1.4  1999/11/12 17:18:39  djs
# Lots of header generation bugfixes
#
# Revision 1.3  1999/11/04 19:05:08  djs
# Finished moving code from tmp_omniidl. Regression tests ok.
#
# Revision 1.2  1999/11/03 17:35:11  djs
# Brought more of the old tmp_omniidl code into the new tree
#

# -----------------------------
# Configuration data
from omniidl_be.cxx import config

# -----------------------------
# Output generation functions
import omniidl_be.cxx.header.defs
import omniidl_be.cxx.header.opers
import omniidl_be.cxx.header.poa
import omniidl_be.cxx.header.tie
import omniidl_be.cxx.header.forward
import omniidl_be.cxx.header.marshal
import omniidl_be.cxx.header.tcstring

from omniidl_be.cxx.header import template

# -----------------------------
# Utility functions
from omniidl_be.cxx import util, id

# -----------------------------
# System functions
import string, re, sys, os.path


def header(stream, filename):
    stream.out(template.header,
               program = config.state['Program Name'],
               library = config.state['Library Version'],
               guard = filename)

def footer(stream):
    stream.out(template.footer)

def defs_fragment(stream, tree):
    """Creates the defs fragment only"""
    filename = config.state['Basename'] + config.state['_DEFS Fragment']
    header(stream, filename)

    # generate the header definitions
    forward = omniidl_be.cxx.header.forward.__init__(stream)
    tree.accept(forward)
    
    # generate the bounded string tcParser stuff
    tcstring = omniidl_be.cxx.header.tcstring.__init__(stream)
    tree.accept(tcstring)

    defs = omniidl_be.cxx.header.defs.__init__(stream)
    tree.accept(defs)

    footer(stream)

def opers_fragment(stream, tree):
    """Creates the opers fragment only"""
    filename = config.state['Basename'] + config.state['_OPERS Fragment']
    header(stream, filename)

    # see o2be_root::produce_hdr and o2be_root::produce_hdr_defs
    opers = omniidl_be.cxx.header.opers.__init__(stream)
    tree.accept(opers)

    marshal = omniidl_be.cxx.header.marshal.__init__(stream)
    tree.accept(marshal)

    footer(stream)

def poa_fragment(stream, tree):
    """Creates the poa fragment only"""
    filename = config.state['Basename'] + config.state['_POA Fragment']
    header(stream, filename)

    poa = omniidl_be.cxx.header.poa.__init__(stream)
    tree.accept(poa)

    footer(stream)

def monolithic(stream, tree):
    """Creates one large header with all definitions inside"""

    guard = id.Name([config.state['Basename']]).guard()

    header(stream, guard)

    # Add in any direct C++ from toplevel pragma if present
    cxx_direct_includes = []
    directive = "hh"
    for pragma in tree.pragmas():
        # ignore all pragmas but those in the main file
        if pragma.file() != tree.file(): continue
        
        if pragma.text()[0:len(directive)] == directive:
            cxx_direct_includes.append(pragma.text()[len(directive)+1:])
            
    includes = util.StringStream()
    # produce #includes for all files included by the IDL
    for include in config.includes:
        # skip the main file
        if tree.file() == include:
            continue
        
        # the old C++ BE makes orb.idl a special case
        # (might now be a redundant test)
        
        # dirname  == directory containing the include file
        # filename == extensionless filename
        # ext      == extension (typically .idl)
        (root, ext) = os.path.splitext(include)
        (dirname, filename) = os.path.split(root)
        # make the C++ header filename
        filename = filename + config.state['HH Suffix']
        # s/\W/_/g
        guardname = id.Name([filename]).guard()

        cxx_include = filename
        if config.state['Keep Include Path']:
            cxx_include = os.path.join(dirname, cxx_include)

        if config.state['Use Quotes']:
            cxx_include = "\"" + cxx_include + "\""
        else:
            cxx_include = "<" + cxx_include + ">"
            
        includes.out(template.main_include,
                     guardname = guardname, filename = cxx_include)

    # see o2be_root::produce_hdr and o2be_root::produce_hdr_defs

    # generate the header definitions
    def forward_dec(stream = stream, tree = tree):
        forward = omniidl_be.cxx.header.forward.__init__(stream)
        tree.accept(forward)

    # generate the bounded string tcParser stuff
    def string_tcparser(stream = stream, tree = tree):
        tcstring = omniidl_be.cxx.header.tcstring.__init__(stream)
        tree.accept(tcstring)

    def main_defs(stream = stream, tree = tree):
        defs = omniidl_be.cxx.header.defs.__init__(stream)
        tree.accept(defs)

    def main_poa(stream = stream, tree = tree):
        # includes inline (non-flat) tie templates
        poa = omniidl_be.cxx.header.poa.__init__(stream)
        tree.accept(poa)

    def other_tie(stream = stream, tree = tree):
        if config.state['Normal Tie'] and config.state['BOA Skeletons']:
            tie = omniidl_be.cxx.header.tie.BOATieTemplates(stream)
            tree.accept(tie)
        
        if config.state['Flattened Tie']:
            tie = omniidl_be.cxx.header.tie.FlatTieTemplates(stream)
            tree.accept(tie)

    # see o2be_root::produce_hdr and o2be_root::produce_hdr_defs
    def main_opers(stream = stream, tree = tree):
        opers = omniidl_be.cxx.header.opers.__init__(stream)
        tree.accept(opers)
        
    def main_marshal(stream = stream, tree = tree):
        marshal = omniidl_be.cxx.header.marshal.__init__(stream)
        tree.accept(marshal)

    # other stuff
    stream.out(template.main,
               includes = str(includes),
               cxx_direct_include = string.join(cxx_direct_includes,"\n"),
               forward_declarations = forward_dec,
               string_tcParser_declarations = string_tcparser,
               defs = main_defs,
               poa = main_poa,
               other_tie = other_tie,
               operators = main_opers,
               marshalling = main_marshal,
               guard = guard)


def run(tree):
    if config.state['Fragment']:
        # build the defs file
        defs_filename = config.state['Basename']         +\
                        config.state['_DEFS Fragment']   +\
                        config.state['HH Suffix']
        defs_stream = util.Stream(open(defs_filename, "w"), 2)
        defs_fragment(defs_stream, tree)

        # build the opers file
        opers_filename = config.state['Basename']        +\
                         config.state['_OPERS Fragment'] +\
                         config.state['HH Suffix']
        opers_stream = util.Stream(open(opers_filename, "w"), 2)
        opers_fragment(opers_stream, tree)

        # build the poa file
        poa_filename = config.state['Basename']          +\
                       config.state['_POA Fragment']     +\
                       config.state['HH Suffix']
        poa_stream = util.Stream(open(poa_filename, "w"), 2)
        poa_fragment(poa_stream, tree)
    else:
        # build the full header file
        header_filename = config.state['Basename']       +\
                          config.state['HH Suffix']
        stream = util.Stream(open(header_filename, "w"), 2)
        # generate one big chunk of header
        monolithic(stream, tree)

