1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "sal/config.h"
28 #include "climaker_share.h"
31 #include "osl/process.h"
32 #include "osl/file.hxx"
33 #include "osl/thread.h"
34 #include "rtl/ustrbuf.hxx"
35 #include "cppuhelper/shlib.hxx"
36 #include "cppuhelper/bootstrap.hxx"
37 #include "com/sun/star/lang/XComponent.hpp"
38 #include "com/sun/star/container/XHierarchicalNameAccess.hpp"
39 #include "com/sun/star/container/XSet.hpp"
40 #include "com/sun/star/reflection/XTypeDescriptionEnumerationAccess.hpp"
41 #include "com/sun/star/uno/XComponentContext.hpp"
42 #include "unoidl/unoidl.hxx"
44 using namespace ::std
;
45 using namespace ::System::Reflection
;
48 using namespace ::rtl
;
49 using namespace ::osl
;
50 using namespace ::com::sun::star
;
51 using namespace ::com::sun::star::uno
;
56 //------------------------------------------------------------------------------
57 static char const s_usingText
[] =
59 "using: climaker <switches> [registry-file-1 registry-file-2 ...]\n"
62 " -O, --out <output-file> output assembly file;\n"
63 " defaults to cli_unotypes.dll if more than one\n"
64 " registry-file is given, else <registry-file>.dll\n"
65 " -T, --types types to be generated (if none is given,\n"
66 " <type1[;type2;...]> then all types of given registries are emitted\n"
67 " -X, --extra <rdb-file> additional rdb to saturate referenced types in\n"
68 " given registry file(s); these types will not be\n"
69 " emitted into the output assembly file\n"
70 " -r, --reference reference metadata from assembly file\n"
72 " -k, --keyfile keyfile needed for strong name\n"
73 " --assembly-version <version> sets assembly version\n"
74 " --assembly-description <text> sets assembly description text\n"
75 " --assembly-product <text> sets assembly product name\n"
76 " --assembly-company <text> sets assembly company\n"
77 " --assembly-copyright <text> sets assembly copyright\n"
78 " --assembly-trademark <text> sets assembly trademark\n"
79 " -v, --verbose verbose output to stdout\n"
80 " -h, --help this message\n"
82 "example: climaker --out cli_mytypes.dll \\\n"
83 " --reference cli_uretypes.dll \\\n"
84 " --extra types.rdb \\\n"
91 sal_uInt32 m_name_length
;
92 sal_Unicode m_short_option
;
96 bool g_verbose
= false;
98 //------------------------------------------------------------------------------
99 static const OptionInfo s_option_infos
[] = {
100 { RTL_CONSTASCII_STRINGPARAM("out"), 'O', true },
101 { RTL_CONSTASCII_STRINGPARAM("types"), 'T', true },
102 { RTL_CONSTASCII_STRINGPARAM("extra"), 'X', true },
103 { RTL_CONSTASCII_STRINGPARAM("reference"), 'r', true },
104 { RTL_CONSTASCII_STRINGPARAM("keyfile"), 'k', true },
105 { RTL_CONSTASCII_STRINGPARAM("delaySign"), 'd', true },
106 { RTL_CONSTASCII_STRINGPARAM("assembly-version"), '\0', true },
107 { RTL_CONSTASCII_STRINGPARAM("assembly-description"), '\0', true },
108 { RTL_CONSTASCII_STRINGPARAM("assembly-product"), '\0', true },
109 { RTL_CONSTASCII_STRINGPARAM("assembly-company"), '\0', true },
110 { RTL_CONSTASCII_STRINGPARAM("assembly-copyright"), '\0', true },
111 { RTL_CONSTASCII_STRINGPARAM("assembly-trademark"), '\0', true },
112 { RTL_CONSTASCII_STRINGPARAM("verbose"), 'v', false },
113 { RTL_CONSTASCII_STRINGPARAM("help"), 'h', false }
116 //==============================================================================
117 static OptionInfo
const * get_option_info(
118 OUString
const & opt
, sal_Unicode copt
= '\0' )
120 for ( sal_Int32 pos
= 0;
121 pos
< (sizeof (s_option_infos
) / sizeof (OptionInfo
));
124 OptionInfo
const & option_info
= s_option_infos
[ pos
];
126 if (opt
.getLength() > 0)
128 if (opt
.equalsAsciiL(
129 option_info
.m_name
, option_info
.m_name_length
) &&
130 (copt
== '\0' || copt
== option_info
.m_short_option
))
137 OSL_ASSERT( copt
!= '\0' );
138 if (copt
== option_info
.m_short_option
)
145 OUStringToOString( opt
, osl_getThreadTextEncoding() ).getStr() );
149 //==============================================================================
150 static bool is_option(
151 OptionInfo
const * option_info
, sal_uInt32
* pIndex
)
153 OSL_ASSERT( option_info
!= 0 );
154 if (osl_getCommandArgCount() <= *pIndex
)
158 osl_getCommandArg( *pIndex
, &arg
.pData
);
159 sal_Int32 len
= arg
.getLength();
161 if (len
< 2 || arg
[ 0 ] != '-')
164 if (len
== 2 && arg
[ 1 ] == option_info
->m_short_option
)
167 #if OSL_DEBUG_LEVEL > 1
169 __FILE__
": identified option \'%c\'", option_info
->m_short_option
);
173 if (arg
[ 1 ] == '-' && rtl_ustr_ascii_compare(
174 arg
.pData
->buffer
+ 2, option_info
->m_name
) == 0)
177 #if OSL_DEBUG_LEVEL > 1
178 OSL_TRACE( __FILE__
": identified option \'%s\'", option_info
->m_name
);
185 //==============================================================================
186 static inline bool read_option(
187 bool * flag
, OptionInfo
const * option_info
, sal_uInt32
* pIndex
)
189 bool ret
= is_option( option_info
, pIndex
);
195 //==============================================================================
196 static bool read_argument(
197 OUString
* pValue
, OptionInfo
const * option_info
, sal_uInt32
* pIndex
)
199 if (is_option( option_info
, pIndex
))
201 if (*pIndex
< osl_getCommandArgCount())
203 osl_getCommandArg( *pIndex
, &pValue
->pData
);
205 #if OSL_DEBUG_LEVEL > 1
207 OUStringToOString( *pValue
, osl_getThreadTextEncoding() ) );
208 OSL_TRACE( __FILE__
": argument value: %s\n", cstr_val
.getStr() );
217 //==============================================================================
218 static OUString
const & path_get_working_dir()
220 static OUString s_workingDir
;
221 if (! s_workingDir
.getLength())
222 osl_getProcessWorkingDir( &s_workingDir
.pData
);
226 //==============================================================================
227 static OUString
path_make_absolute_file_url( OUString
const & path
)
230 oslFileError rc
= osl_getFileURLFromSystemPath(
231 path
.pData
, &file_url
.pData
);
232 if (osl_File_E_None
== rc
)
235 rc
= osl_getAbsoluteFileURL(
236 path_get_working_dir().pData
, file_url
.pData
, &abs
.pData
);
237 if (osl_File_E_None
== rc
)
243 throw RuntimeException(
244 "cannot make absolute: " + file_url
,
245 css::uno::Reference
< XInterface
>() );
250 throw RuntimeException(
251 "cannot get file url from system path: " + path
,
252 css::uno::Reference
< XInterface
>() );
258 using namespace ::climaker
;
260 //##############################################################################
263 sal_uInt32 nCount
= osl_getCommandArgCount();
266 printf( s_usingText
);
271 css::uno::Reference
< XComponentContext
> xContext
;
275 OptionInfo
const * info_help
=
276 get_option_info( "help" );
277 OptionInfo
const * info_verbose
=
278 get_option_info( "verbose" );
279 OptionInfo
const * info_out
=
280 get_option_info( "out" );
281 OptionInfo
const * info_types
=
282 get_option_info( "types" );
283 OptionInfo
const * info_reference
=
284 get_option_info( "reference" );
285 OptionInfo
const * info_extra
=
286 get_option_info( "extra" );
287 OptionInfo
const * info_keyfile
=
288 get_option_info( "keyfile" );
289 OptionInfo
const * info_delaySign
=
290 get_option_info( "delaySign" );
291 OptionInfo
const * info_version
=
292 get_option_info( "assembly-version" );
293 OptionInfo
const * info_product
=
294 get_option_info( "assembly-product" );
295 OptionInfo
const * info_description
=
296 get_option_info( "assembly-description" );
297 OptionInfo
const * info_company
=
298 get_option_info( "assembly-company" );
299 OptionInfo
const * info_copyright
=
300 get_option_info( "assembly-copyright" );
301 OptionInfo
const * info_trademark
=
302 get_option_info( "assembly-trademark" );
305 vector
< OUString
> mandatory_registries
;
306 vector
< OUString
> extra_registries
;
307 vector
< OUString
> extra_assemblies
;
308 vector
< OUString
> explicit_types
;
309 OUString version
, product
, description
, company
, copyright
, trademark
,
313 for ( sal_uInt32 nPos
= 0; nPos
< nCount
; )
316 if (is_option( info_help
, &nPos
))
318 printf( s_usingText
);
321 else if (read_argument( &cmd_arg
, info_types
, &nPos
))
326 explicit_types
.push_back(
327 cmd_arg
.getToken( 0, ';', index
) );
331 else if (read_argument( &cmd_arg
, info_extra
, &nPos
))
333 extra_registries
.push_back(
334 path_make_absolute_file_url( cmd_arg
) );
336 else if (read_argument( &cmd_arg
, info_reference
, &nPos
))
338 extra_assemblies
.push_back(
339 path_make_absolute_file_url( cmd_arg
) );
341 else if (!read_option( &g_verbose
, info_verbose
, &nPos
) &&
342 !read_argument( &output
, info_out
, &nPos
) &&
343 !read_argument( &version
, info_version
, &nPos
) &&
344 !read_argument( &description
, info_description
, &nPos
) &&
345 !read_argument( &product
, info_product
, &nPos
) &&
346 !read_argument( &company
, info_company
, &nPos
) &&
347 !read_argument( ©right
, info_copyright
, &nPos
) &&
348 !read_argument( &trademark
, info_trademark
, &nPos
) &&
349 !read_argument( &keyfile
, info_keyfile
, &nPos
) &&
350 !read_argument( &delaySign
, info_delaySign
, &nPos
))
352 if ( osl_getCommandArg( nPos
, &cmd_arg
.pData
) !=
358 cmd_arg
= cmd_arg
.trim();
359 if (cmd_arg
.getLength() > 0)
361 if (cmd_arg
[ 0 ] == '-') // is option
363 OptionInfo
const * option_info
= 0;
364 if (cmd_arg
.getLength() > 2 &&
368 option_info
= get_option_info(
369 cmd_arg
.copy( 2 ), '\0' );
371 else if (cmd_arg
.getLength() == 2 &&
375 option_info
= get_option_info(
376 OUString(), cmd_arg
[ 1 ] );
378 if (option_info
== 0)
382 RTL_CONSTASCII_STRINGPARAM("unknown option ") );
383 buf
.append( cmd_arg
);
384 buf
.appendAscii( RTL_CONSTASCII_STRINGPARAM(
385 "! Use climaker --help "
386 "to print all options.") );
387 throw RuntimeException(
388 buf
.makeStringAndClear(),
389 css::uno::Reference
< XInterface
>() );
393 OSL_FAIL( "unhandled valid option?!" );
394 if (option_info
->m_has_argument
)
400 mandatory_registries
.push_back(
401 path_make_absolute_file_url( cmd_arg
) );
408 xContext
= ::cppu::defaultBootstrap_InitialComponentContext();
409 css::uno::Reference
< container::XHierarchicalNameAccess
> xTDmgr(
410 xContext
->getValueByName(
411 "/singletons/com.sun.star.reflection."
412 "theTypeDescriptionManager" ),
415 // The registries are consumed twice, once to insert them into the
416 // TypeDescriptionManager so that TypeEmitter can work on
417 // css.star.reflection.XTypeDescription representation, and once
418 // directly as unoidl::Provider instances to keep track which types are
419 // coming from the mandatory registries for the "no explicit types
420 // given" case (which iterates over the full TypeDescriptionManager
421 // now); a welcome clean-up would be to make TypeEmitter work on
422 // unoidl::Entity directly like the other codemakers:
423 css::uno::Reference
< container::XSet
> xSet( xTDmgr
, UNO_QUERY_THROW
);
424 rtl::Reference
< unoidl::Manager
> unoidlMgr(new unoidl::Manager
);
425 std::vector
< rtl::Reference
< unoidl::Provider
> > unoidlMandatoryProvs
;
426 for (vector
< OUString
>::iterator
i(extra_registries
.begin());
427 i
!= extra_registries
.end(); ++i
)
429 xSet
->insert(makeAny(*i
));
430 unoidlMgr
->addProvider(unoidl::loadProvider(unoidlMgr
, *i
));
432 for (vector
< OUString
>::iterator
i(mandatory_registries
.begin());
433 i
!= mandatory_registries
.end(); ++i
)
435 xSet
->insert(makeAny(*i
));
436 rtl::Reference
< unoidl::Provider
> prov(
437 unoidl::loadProvider(unoidlMgr
, *i
));
438 unoidlMgr
->addProvider(prov
);
439 unoidlMandatoryProvs
.push_back(prov
);
442 if (0 == output
.getLength()) // no output file specified
444 // if only one rdb has been given, then take rdb name
445 if (1 == mandatory_registries
.size())
447 output
= mandatory_registries
[ 0 ];
448 output
= output
.copy( output
.lastIndexOf( '/' ) +1 );
449 sal_Int32 dot
= output
.lastIndexOf( '.' );
451 output
= output
.copy( 0, dot
);
455 output
= "cli_unotypes";
458 output
= path_make_absolute_file_url( output
);
459 sal_Int32 slash
= output
.lastIndexOf( '/' );
460 OUString sys_output_dir
;
461 if (FileBase::E_None
!= FileBase::getSystemPathFromFileURL(
462 output
.copy( 0, slash
), sys_output_dir
))
464 throw RuntimeException(
465 "cannot get system path from file url " +
466 output
.copy( 0, slash
),
467 css::uno::Reference
< XInterface
>() );
469 OUString
filename( output
.copy( slash
+1 ) );
470 sal_Int32 dot
= filename
.lastIndexOf( '.' );
471 OUString
name( filename
);
472 if (dot
< 0) // has no extension
475 name
= name
.copy( 0, dot
);
476 ::System::String
^ output_dir
= ustring_to_String( sys_output_dir
);
477 ::System::String
^ output_file
= ustring_to_String( filename
);
479 //Get the key pair for making a strong name
480 StrongNameKeyPair
^ kp
= nullptr;
481 if (keyfile
.getLength() > 0)
483 ::System::String
^ sKeyFile
= ustring_to_String(keyfile
);
485 System::IO::FileStream
^ fs
= gcnew
System::IO::FileStream(
486 sKeyFile
, System::IO::FileMode::Open
);
487 kp
= gcnew
StrongNameKeyPair(fs
);
490 catch (System::IO::FileNotFoundException
^ )
492 throw Exception("Could not find the keyfile. Verify the --keyfile argument!", 0);
499 ::System::Console::Write(
500 "> no key file specified. Cannot create strong name!\n");
503 // setup assembly info: xxx todo set more? e.g. avoid strong versioning
504 AssemblyName
^ assembly_name
= gcnew
AssemblyName();
505 assembly_name
->CodeBase
= output_dir
;
506 assembly_name
->Name
= gcnew ::System::String(name
.getStr());
508 assembly_name
->KeyPair
= kp
;
510 if (version
.getLength() != 0)
512 assembly_name
->Version
=
513 gcnew ::System::Version( ustring_to_String( version
) );
517 ::System::AppDomain
^ current_appdomain
=
518 ::System::AppDomain::CurrentDomain
;
520 // Weird warning from this statement
521 // warning C4538: 'cli::array<Type> ^' : const/volatile qualifiers on this type are not supported
522 // Could be a compiler bug, says http://stackoverflow.com/questions/12151060/seemingly-inappropriate-compilation-warning-with-c-cli
523 #pragma warning (push)
524 #pragma warning (disable: 4538)
526 Emit::AssemblyBuilder
^ assembly_builder
=
527 current_appdomain
->DefineDynamicAssembly(
528 assembly_name
, Emit::AssemblyBuilderAccess::Save
, output_dir
);
529 #pragma warning (pop)
531 if (product
.getLength() != 0)
533 array
< ::System::Type
^>^ params
= gcnew array
< ::System::Type
^> (1);
534 array
< ::System::Object
^>^args
= gcnew array
< ::System::Object
^>(1);
535 params
[ 0 ] = ::System::String::typeid;
536 args
[ 0 ] = ustring_to_String( product
);
537 assembly_builder
->SetCustomAttribute(
538 gcnew
Emit::CustomAttributeBuilder(
539 (AssemblyProductAttribute::typeid)->GetConstructor(
542 if (description
.getLength() != 0)
544 array
< ::System::Type
^>^ params
= gcnew array
< ::System::Type
^>(1);
545 array
< ::System::Object
^>^ args
= gcnew array
< ::System::Object
^>(1);
546 params
[ 0 ] = ::System::String::typeid;
547 args
[ 0 ] = ustring_to_String( description
);
548 assembly_builder
->SetCustomAttribute(
549 gcnew
Emit::CustomAttributeBuilder(
550 (AssemblyDescriptionAttribute::typeid)->GetConstructor(
553 if (company
.getLength() != 0)
555 array
< ::System::Type
^>^ params
= gcnew array
< ::System::Type
^>(1);
556 array
< ::System::Object
^>^ args
= gcnew array
< ::System::Object
^>(1);
557 params
[ 0 ] = ::System::String::typeid;
558 args
[ 0 ] = ustring_to_String( company
);
559 assembly_builder
->SetCustomAttribute(
560 gcnew
Emit::CustomAttributeBuilder(
561 (AssemblyCompanyAttribute::typeid)->GetConstructor(
564 if (copyright
.getLength() != 0)
566 array
< ::System::Type
^>^ params
= gcnew array
< ::System::Type
^>(1);
567 array
< ::System::Object
^>^ args
= gcnew array
< ::System::Object
^>(1);
568 params
[ 0 ] = ::System::String::typeid;
569 args
[ 0 ] = ustring_to_String( copyright
);
570 assembly_builder
->SetCustomAttribute(
571 gcnew
Emit::CustomAttributeBuilder(
572 (AssemblyCopyrightAttribute::typeid)->GetConstructor(
575 if (trademark
.getLength() != 0)
577 array
< ::System::Type
^>^ params
= gcnew array
< ::System::Type
^>(1);
578 array
< ::System::Object
^>^ args
= gcnew array
< ::System::Object
^>(1);
579 params
[ 0 ] = ::System::String::typeid;
580 args
[ 0 ] = ustring_to_String( trademark
);
581 assembly_builder
->SetCustomAttribute(
582 gcnew
Emit::CustomAttributeBuilder(
583 (AssemblyTrademarkAttribute::typeid)->GetConstructor(
587 // load extra assemblies
588 array
<Assembly
^>^ assemblies
=
589 gcnew array
<Assembly
^>(extra_assemblies
.size());
590 for ( size_t pos
= 0; pos
< extra_assemblies
.size(); ++pos
)
592 assemblies
[ pos
] = Assembly::LoadFrom(
593 ustring_to_String( extra_assemblies
[ pos
] ) );
597 TypeEmitter
^ type_emitter
= gcnew
TypeEmitter(
598 assembly_builder
->DefineDynamicModule( output_file
), assemblies
);
599 // add handler resolving assembly's types
600 ::System::ResolveEventHandler
^ type_resolver
=
601 gcnew ::System::ResolveEventHandler(
602 type_emitter
, &TypeEmitter::type_resolve
);
603 current_appdomain
->TypeResolve
+= type_resolver
;
605 // and emit types to it
606 if (explicit_types
.empty())
608 css::uno::Reference
< reflection::XTypeDescriptionEnumeration
> xTD_enum(
609 css::uno::Reference
< reflection::XTypeDescriptionEnumerationAccess
>(
610 xTDmgr
, UNO_QUERY_THROW
)
611 ->createTypeDescriptionEnumeration(
612 OUString() /* all IDL modules */,
613 Sequence
< TypeClass
>() /* all classes of types */,
614 reflection::TypeDescriptionSearchDepth_INFINITE
) );
615 while (xTD_enum
->hasMoreElements())
617 css::uno::Reference
< reflection::XTypeDescription
> td(
618 xTD_enum
->nextTypeDescription());
619 OUString
name(td
->getName());
621 for (std::vector
< rtl::Reference
< unoidl::Provider
> >::iterator
622 i(unoidlMandatoryProvs
.begin());
623 i
!= unoidlMandatoryProvs
.end(); ++i
)
625 if ((*i
)->findEntity(name
).is()) {
631 type_emitter
->get_type(td
);
637 for ( size_t nPos
= explicit_types
.size(); nPos
--; )
639 type_emitter
->get_type(
640 css::uno::Reference
< reflection::XTypeDescription
>(
641 xTDmgr
->getByHierarchicalName( explicit_types
[ nPos
] ),
645 type_emitter
->~TypeEmitter();
650 // Bogus: warning C4564: method 'CheckInvalidPathChars' of class 'System::IO::Path' defines unsupported default parameter 'checkAdditional'
651 #pragma warning (push)
652 #pragma warning (disable: 4564)
654 ::System::Console::Write(
655 "> saving assembly {0}{1}{2}...",
657 gcnew ::System::String(
658 ::System::IO::Path::DirectorySeparatorChar
, 1 ),
661 #pragma warning (pop)
664 assembly_builder
->Save( output_file
);
667 ::System::Console::WriteLine( "ok." );
669 current_appdomain
->TypeResolve
-= type_resolver
;
671 catch (unoidl::NoSuchFileException
& e
)
673 std::cerr
<< "ERROR: No such file <" << e
.getUri() << ">\n";
676 catch (unoidl::FileFormatException
& e
)
679 << "ERROR: Bad format of <" << e
.getUri() << ">, \""
680 << e
.getDetail() << "\"\n";
683 catch (Exception
& exc
)
686 OUStringToOString( exc
.Message
, osl_getThreadTextEncoding() ) );
688 stderr
, "\n> error: %s\n> dying abnormally...\n", msg
.getStr() );
691 catch (::System::Exception
^ exc
)
693 OString
msg( OUStringToOString(
694 String_to_ustring( exc
->ToString() ),
695 osl_getThreadTextEncoding() ) );
698 "\n> error: .NET exception occurred: %s\n> dying abnormally...",
705 css::uno::Reference
< lang::XComponent
> xComp( xContext
, UNO_QUERY
);
709 catch (Exception
& exc
)
712 OUStringToOString( exc
.Message
, osl_getThreadTextEncoding() ) );
715 "\n> error disposing component context: %s\n"
716 "> dying abnormally...\n",
724 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */