Bump version to 6.4-15
[LibreOffice.git] / cli_ure / source / climaker / climaker_app.cxx
blob97b1a5ce9bcee1520b6b59abfa67f2cf3f9d21c4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
22 #include <cstdlib>
23 #include <iostream>
24 #include <stdio.h>
25 #include <vector>
27 #include "climaker_share.h"
29 #include "sal/main.h"
30 #include "osl/process.h"
31 #include "osl/file.hxx"
32 #include "osl/thread.h"
33 #include "rtl/ustrbuf.hxx"
34 #include "cppuhelper/bootstrap.hxx"
35 #include "com/sun/star/lang/XComponent.hpp"
36 #include "com/sun/star/container/XHierarchicalNameAccess.hpp"
37 #include "com/sun/star/container/XSet.hpp"
38 #include "com/sun/star/reflection/XTypeDescriptionEnumerationAccess.hpp"
39 #include "com/sun/star/uno/XComponentContext.hpp"
40 #include "unoidl/unoidl.hxx"
42 using namespace ::std;
43 using namespace ::System::Reflection;
46 using namespace ::osl;
47 using namespace ::com::sun::star;
48 using namespace ::com::sun::star::uno;
50 namespace climaker
54 static char const s_usingText [] =
55 "\n"
56 "using: climaker <switches> [registry-file-1 registry-file-2 ...]\n"
57 "\n"
58 "switches:\n"
59 " -O, --out <output-file> output assembly file;\n"
60 " defaults to cli_unotypes.dll if more than one\n"
61 " registry-file is given, else <registry-file>.dll\n"
62 " -T, --types types to be generated (if none is given,\n"
63 " <type1[;type2;...]> then all types of given registries are emitted\n"
64 " -X, --extra <rdb-file> additional rdb to saturate referenced types in\n"
65 " given registry file(s); these types will not be\n"
66 " emitted into the output assembly file\n"
67 " -r, --reference reference metadata from assembly file\n"
68 " <assembly-file>\n"
69 " -k, --keyfile keyfile needed for strong name\n"
70 " --assembly-version <version> sets assembly version\n"
71 " --assembly-description <text> sets assembly description text\n"
72 " --assembly-product <text> sets assembly product name\n"
73 " --assembly-company <text> sets assembly company\n"
74 " --assembly-copyright <text> sets assembly copyright\n"
75 " --assembly-trademark <text> sets assembly trademark\n"
76 " -v, --verbose verbose output to stdout\n"
77 " -h, --help this message\n"
78 "\n"
79 "example: climaker --out cli_mytypes.dll \\\n"
80 " --reference cli_uretypes.dll \\\n"
81 " --extra types.rdb \\\n"
82 " mytypes.rdb\n"
83 "\n";
85 struct OptionInfo
87 char const * m_name;
88 sal_uInt32 m_name_length;
89 sal_Unicode m_short_option;
90 bool m_has_argument;
93 bool g_bVerbose = false;
96 static const OptionInfo s_option_infos [] = {
97 { RTL_CONSTASCII_STRINGPARAM("out"), 'O', true },
98 { RTL_CONSTASCII_STRINGPARAM("types"), 'T', true },
99 { RTL_CONSTASCII_STRINGPARAM("extra"), 'X', true },
100 { RTL_CONSTASCII_STRINGPARAM("reference"), 'r', true },
101 { RTL_CONSTASCII_STRINGPARAM("keyfile"), 'k', true },
102 { RTL_CONSTASCII_STRINGPARAM("delaySign"), 'd', true },
103 { RTL_CONSTASCII_STRINGPARAM("assembly-version"), '\0', true },
104 { RTL_CONSTASCII_STRINGPARAM("assembly-description"), '\0', true },
105 { RTL_CONSTASCII_STRINGPARAM("assembly-product"), '\0', true },
106 { RTL_CONSTASCII_STRINGPARAM("assembly-company"), '\0', true },
107 { RTL_CONSTASCII_STRINGPARAM("assembly-copyright"), '\0', true },
108 { RTL_CONSTASCII_STRINGPARAM("assembly-trademark"), '\0', true },
109 { RTL_CONSTASCII_STRINGPARAM("verbose"), 'v', false },
110 { RTL_CONSTASCII_STRINGPARAM("help"), 'h', false }
114 static OptionInfo const * get_option_info(
115 OUString const & opt, sal_Unicode copt = '\0' )
117 for ( sal_Int32 pos = 0;
118 pos < (sizeof (s_option_infos) / sizeof (OptionInfo));
119 ++pos )
121 OptionInfo const & option_info = s_option_infos[ pos ];
123 if (opt.getLength() > 0)
125 if (opt.equalsAsciiL(
126 option_info.m_name, option_info.m_name_length ) &&
127 (copt == '\0' || copt == option_info.m_short_option))
129 return &option_info;
132 else
134 OSL_ASSERT( copt != '\0' );
135 if (copt == option_info.m_short_option)
137 return &option_info;
141 OSL_FAIL(
142 OUStringToOString( opt, osl_getThreadTextEncoding() ).getStr() );
143 return 0;
147 static bool is_option(
148 OptionInfo const * option_info, sal_uInt32 * pIndex )
150 OSL_ASSERT( option_info != 0 );
151 if (osl_getCommandArgCount() <= *pIndex)
152 return false;
154 OUString arg;
155 osl_getCommandArg( *pIndex, &arg.pData );
156 sal_Int32 len = arg.getLength();
158 if (len < 2 || arg[ 0 ] != '-')
159 return false;
161 if (len == 2 && arg[ 1 ] == option_info->m_short_option)
163 ++(*pIndex);
164 return true;
166 if (arg[ 1 ] == '-' && rtl_ustr_ascii_compare(
167 arg.pData->buffer + 2, option_info->m_name ) == 0)
169 ++(*pIndex);
170 return true;
172 return false;
176 static inline bool read_option(
177 bool * flag, OptionInfo const * option_info, sal_uInt32 * pIndex )
179 bool ret = is_option( option_info, pIndex );
180 if (ret)
181 *flag = true;
182 return ret;
186 static bool read_argument(
187 OUString * pValue, OptionInfo const * option_info, sal_uInt32 * pIndex )
189 if (is_option( option_info, pIndex ))
191 if (*pIndex < osl_getCommandArgCount())
193 osl_getCommandArg( *pIndex, &pValue->pData );
194 ++(*pIndex);
195 return true;
197 --(*pIndex);
199 return false;
203 static OUString const & path_get_working_dir()
205 static OUString s_workingDir;
206 if (! s_workingDir.getLength())
207 osl_getProcessWorkingDir( &s_workingDir.pData );
208 return s_workingDir;
212 static OUString path_make_absolute_file_url( OUString const & path )
214 OUString file_url;
215 oslFileError rc = osl_getFileURLFromSystemPath(
216 path.pData, &file_url.pData );
217 if (osl_File_E_None == rc)
219 OUString abs;
220 rc = osl_getAbsoluteFileURL(
221 path_get_working_dir().pData, file_url.pData, &abs.pData );
222 if (osl_File_E_None == rc)
224 return abs;
226 else
228 throw RuntimeException(
229 "cannot make absolute: " + file_url );
232 else
234 throw RuntimeException(
235 "cannot get file url from system path: " + path );
241 using namespace ::climaker;
244 SAL_IMPLEMENT_MAIN()
246 sal_uInt32 nCount = osl_getCommandArgCount();
247 if (0 == nCount)
249 puts( s_usingText );
250 return 0;
253 int ret = 0;
254 css::uno::Reference< XComponentContext > xContext;
258 OptionInfo const * info_help =
259 get_option_info( "help" );
260 OptionInfo const * info_verbose =
261 get_option_info( "verbose" );
262 OptionInfo const * info_out =
263 get_option_info( "out" );
264 OptionInfo const * info_types =
265 get_option_info( "types" );
266 OptionInfo const * info_reference =
267 get_option_info( "reference" );
268 OptionInfo const * info_extra =
269 get_option_info( "extra" );
270 OptionInfo const * info_keyfile =
271 get_option_info( "keyfile" );
272 OptionInfo const * info_delaySign =
273 get_option_info( "delaySign" );
274 OptionInfo const * info_version =
275 get_option_info( "assembly-version" );
276 OptionInfo const * info_product =
277 get_option_info( "assembly-product" );
278 OptionInfo const * info_description =
279 get_option_info( "assembly-description" );
280 OptionInfo const * info_company =
281 get_option_info( "assembly-company" );
282 OptionInfo const * info_copyright =
283 get_option_info( "assembly-copyright" );
284 OptionInfo const * info_trademark =
285 get_option_info( "assembly-trademark" );
287 OUString output;
288 vector< OUString > mandatory_registries;
289 vector< OUString > extra_registries;
290 vector< OUString > extra_assemblies;
291 vector< OUString > explicit_types;
292 OUString version, product, description, company, copyright, trademark,
293 keyfile, delaySign;
295 OUString cmd_arg;
296 for ( sal_uInt32 nPos = 0; nPos < nCount; )
298 // options
299 if (is_option( info_help, &nPos ))
301 puts( s_usingText );
302 return 0;
304 else if (read_argument( &cmd_arg, info_types, &nPos ))
306 sal_Int32 index = 0;
309 explicit_types.push_back(
310 cmd_arg.getToken( 0, ';', index ) );
312 while (index >= 0);
314 else if (read_argument( &cmd_arg, info_extra, &nPos ))
316 extra_registries.push_back(
317 path_make_absolute_file_url( cmd_arg ) );
319 else if (read_argument( &cmd_arg, info_reference, &nPos ))
321 extra_assemblies.push_back(
322 path_make_absolute_file_url( cmd_arg ) );
324 else if (!read_option( &g_bVerbose, info_verbose, &nPos ) &&
325 !read_argument( &output, info_out, &nPos ) &&
326 !read_argument( &version, info_version, &nPos ) &&
327 !read_argument( &description, info_description, &nPos ) &&
328 !read_argument( &product, info_product, &nPos ) &&
329 !read_argument( &company, info_company, &nPos ) &&
330 !read_argument( &copyright, info_copyright, &nPos ) &&
331 !read_argument( &trademark, info_trademark, &nPos ) &&
332 !read_argument( &keyfile, info_keyfile, &nPos ) &&
333 !read_argument( &delaySign, info_delaySign, &nPos ))
335 osl_getCommandArg( nPos, &cmd_arg.pData );
336 ++nPos;
337 cmd_arg = cmd_arg.trim();
338 if (cmd_arg.getLength() > 0)
340 if (cmd_arg[ 0 ] == '-') // is option
342 OptionInfo const * option_info = 0;
343 if (cmd_arg.getLength() > 2 &&
344 cmd_arg[ 1 ] == '-')
346 // long option
347 option_info = get_option_info(
348 cmd_arg.copy( 2 ), '\0' );
350 else if (cmd_arg.getLength() == 2 &&
351 cmd_arg[ 1 ] != '-')
353 // short option
354 option_info = get_option_info(
355 OUString(), cmd_arg[ 1 ] );
357 if (option_info == 0)
359 throw RuntimeException("unknown option " + cmd_arg + "! Use climaker --help to print all options.");
361 else
363 OSL_FAIL( "unhandled valid option?!" );
364 if (option_info->m_has_argument)
365 ++nPos;
368 else
370 mandatory_registries.push_back(
371 path_make_absolute_file_url( cmd_arg ) );
377 // bootstrap uno
378 xContext = ::cppu::defaultBootstrap_InitialComponentContext();
379 css::uno::Reference< container::XHierarchicalNameAccess > xTDmgr(
380 xContext->getValueByName(
381 "/singletons/com.sun.star.reflection."
382 "theTypeDescriptionManager" ),
383 UNO_QUERY_THROW );
385 // The registries are consumed twice, once to insert them into the
386 // TypeDescriptionManager so that TypeEmitter can work on
387 // css.star.reflection.XTypeDescription representation, and once
388 // directly as unoidl::Provider instances to keep track which types are
389 // coming from the mandatory registries for the "no explicit types
390 // given" case (which iterates over the full TypeDescriptionManager
391 // now); a welcome clean-up would be to make TypeEmitter work on
392 // unoidl::Entity directly like the other codemakers:
393 css::uno::Reference< container::XSet > xSet( xTDmgr, UNO_QUERY_THROW );
394 rtl::Reference unoidlMgr(new unoidl::Manager);
395 std::vector< rtl::Reference< unoidl::Provider > > unoidlMandatoryProvs;
396 for (auto& rRegistry : extra_registries)
398 xSet->insert(makeAny(rRegistry));
399 unoidlMgr->addProvider(rRegistry);
401 for (auto& rRegistry : mandatory_registries)
403 xSet->insert(makeAny(rRegistry));
404 rtl::Reference< unoidl::Provider > prov(unoidlMgr->addProvider(rRegistry));
405 unoidlMandatoryProvs.push_back(prov);
408 if (0 == output.getLength()) // no output file specified
410 // if only one rdb has been given, then take rdb name
411 if (1 == mandatory_registries.size())
413 output = mandatory_registries[ 0 ];
414 output = output.copy( output.lastIndexOf( '/' ) +1 );
415 sal_Int32 dot = output.lastIndexOf( '.' );
416 if (dot > 0)
417 output = output.copy( 0, dot );
419 else
421 output = "cli_unotypes";
424 output = path_make_absolute_file_url( output );
425 sal_Int32 slash = output.lastIndexOf( '/' );
426 OUString sys_output_dir;
427 if (FileBase::E_None != FileBase::getSystemPathFromFileURL(
428 output.copy( 0, slash ), sys_output_dir ))
430 throw RuntimeException(
431 "cannot get system path from file url " +
432 output.copy( 0, slash ) );
434 OUString filename( output.copy( slash +1 ) );
435 sal_Int32 dot = filename.lastIndexOf( '.' );
436 OUString name( filename );
437 if (dot < 0) // has no extension
438 filename += ".dll";
439 else
440 name = name.copy( 0, dot );
441 ::System::String ^ output_dir = ustring_to_String( sys_output_dir );
442 ::System::String ^ output_file = ustring_to_String( filename );
444 //Get the key pair for making a strong name
445 StrongNameKeyPair^ kp = nullptr;
446 if (keyfile.getLength() > 0)
448 ::System::String ^ sKeyFile = ustring_to_String(keyfile);
449 try {
450 System::IO::FileStream^ fs = gcnew System::IO::FileStream(
451 sKeyFile, System::IO::FileMode::Open,
452 System::IO::FileAccess::Read, System::IO::FileShare::Read);
453 kp = gcnew StrongNameKeyPair(fs);
454 fs->Close();
456 catch (System::IO::FileNotFoundException ^ )
458 throw Exception("Could not find the keyfile. Verify the --keyfile argument!", 0);
461 else
463 if (g_bVerbose)
465 ::System::Console::Write(
466 "> no key file specified. Cannot create strong name!\n");
469 // setup assembly info: xxx todo set more? e.g. avoid strong versioning
470 AssemblyName ^ assembly_name = gcnew AssemblyName();
471 assembly_name->CodeBase = output_dir;
472 assembly_name->Name = gcnew ::System::String(
473 reinterpret_cast<wchar_t const *>(name.getStr()));
474 if (kp != nullptr)
475 assembly_name->KeyPair= kp;
477 if (version.getLength() != 0)
479 assembly_name->Version=
480 gcnew ::System::Version( ustring_to_String( version ) );
483 // app domain
484 ::System::AppDomain ^ current_appdomain =
485 ::System::AppDomain::CurrentDomain;
487 // Weird warning from this statement
488 // warning C4538: 'cli::array<Type> ^' : const/volatile qualifiers on this type are not supported
489 // Could be a compiler bug, says http://stackoverflow.com/questions/12151060/seemingly-inappropriate-compilation-warning-with-c-cli
490 #pragma warning (push)
491 #pragma warning (disable: 4538)
492 // target assembly
493 Emit::AssemblyBuilder ^ assembly_builder =
494 current_appdomain->DefineDynamicAssembly(
495 assembly_name, Emit::AssemblyBuilderAccess::Save, output_dir );
496 #pragma warning (pop)
498 if (product.getLength() != 0)
500 cli::array< ::System::Type^>^ params = gcnew cli::array< ::System::Type^> (1);
501 cli::array< ::System::Object^>^args = gcnew cli::array< ::System::Object^>(1);
502 params[ 0 ] = ::System::String::typeid;
503 args[ 0 ] = ustring_to_String( product );
504 assembly_builder->SetCustomAttribute(
505 gcnew Emit::CustomAttributeBuilder(
506 (AssemblyProductAttribute::typeid)->GetConstructor(
507 params ), args ) );
509 if (description.getLength() != 0)
511 cli::array< ::System::Type^>^ params = gcnew cli::array< ::System::Type^>(1);
512 cli::array< ::System::Object^>^ args = gcnew cli::array< ::System::Object^>(1);
513 params[ 0 ] = ::System::String::typeid;
514 args[ 0 ] = ustring_to_String( description );
515 assembly_builder->SetCustomAttribute(
516 gcnew Emit::CustomAttributeBuilder(
517 (AssemblyDescriptionAttribute::typeid)->GetConstructor(
518 params ), args ) );
520 if (company.getLength() != 0)
522 cli::array< ::System::Type^>^ params = gcnew cli::array< ::System::Type^>(1);
523 cli::array< ::System::Object^>^ args = gcnew cli::array< ::System::Object^>(1);
524 params[ 0 ] = ::System::String::typeid;
525 args[ 0 ] = ustring_to_String( company );
526 assembly_builder->SetCustomAttribute(
527 gcnew Emit::CustomAttributeBuilder(
528 (AssemblyCompanyAttribute::typeid)->GetConstructor(
529 params ), args ) );
531 if (copyright.getLength() != 0)
533 cli::array< ::System::Type^>^ params = gcnew cli::array< ::System::Type^>(1);
534 cli::array< ::System::Object^>^ args = gcnew cli::array< ::System::Object^>(1);
535 params[ 0 ] = ::System::String::typeid;
536 args[ 0 ] = ustring_to_String( copyright );
537 assembly_builder->SetCustomAttribute(
538 gcnew Emit::CustomAttributeBuilder(
539 (AssemblyCopyrightAttribute::typeid)->GetConstructor(
540 params ), args ) );
542 if (trademark.getLength() != 0)
544 cli::array< ::System::Type^>^ params = gcnew cli::array< ::System::Type^>(1);
545 cli::array< ::System::Object^>^ args = gcnew cli::array< ::System::Object^>(1);
546 params[ 0 ] = ::System::String::typeid;
547 args[ 0 ] = ustring_to_String( trademark );
548 assembly_builder->SetCustomAttribute(
549 gcnew Emit::CustomAttributeBuilder(
550 (AssemblyTrademarkAttribute::typeid)->GetConstructor(
551 params ), args ) );
554 // load extra assemblies
555 cli::array<Assembly^>^ assemblies =
556 gcnew cli::array<Assembly^>(extra_assemblies.size());
557 for ( size_t pos = 0; pos < extra_assemblies.size(); ++pos )
559 assemblies[ pos ] = Assembly::LoadFrom(
560 ustring_to_String( extra_assemblies[ pos ] ) );
563 // type emitter
564 TypeEmitter ^ type_emitter = gcnew TypeEmitter(
565 assembly_builder->DefineDynamicModule( output_file ), assemblies );
566 // add handler resolving assembly's types
567 ::System::ResolveEventHandler ^ type_resolver =
568 gcnew ::System::ResolveEventHandler(
569 type_emitter, &TypeEmitter::type_resolve );
570 current_appdomain->TypeResolve += type_resolver;
572 // and emit types to it
573 if (explicit_types.empty())
575 css::uno::Reference< reflection::XTypeDescriptionEnumeration > xTD_enum(
576 css::uno::Reference< reflection::XTypeDescriptionEnumerationAccess >(
577 xTDmgr, UNO_QUERY_THROW )
578 ->createTypeDescriptionEnumeration(
579 OUString() /* all IDL modules */,
580 Sequence< TypeClass >() /* all classes of types */,
581 reflection::TypeDescriptionSearchDepth_INFINITE ) );
582 while (xTD_enum->hasMoreElements())
584 css::uno::Reference< reflection::XTypeDescription > td(
585 xTD_enum->nextTypeDescription());
586 OUString name(td->getName());
587 bool bEmit = std::any_of(unoidlMandatoryProvs.begin(), unoidlMandatoryProvs.end(),
588 [&name](rtl::Reference<unoidl::Provider>& rProv) { return rProv->findEntity(name).is(); });
589 if (bEmit) {
590 type_emitter->get_type(td);
594 else
596 for ( size_t nPos = explicit_types.size(); nPos--; )
598 type_emitter->get_type(
599 css::uno::Reference< reflection::XTypeDescription >(
600 xTDmgr->getByHierarchicalName( explicit_types[ nPos ] ),
601 UNO_QUERY_THROW ) );
604 type_emitter->~TypeEmitter();
606 if (g_bVerbose)
608 ::System::Console::Write(
609 "> saving assembly {0}{1}{2}...",
610 output_dir,
611 gcnew ::System::String(
612 ::System::IO::Path::DirectorySeparatorChar, 1 ),
613 output_file );
615 assembly_builder->Save( output_file );
616 if (g_bVerbose)
618 ::System::Console::WriteLine( "ok." );
620 current_appdomain->TypeResolve -= type_resolver;
622 catch (unoidl::NoSuchFileException & e)
624 std::cerr << "ERROR: No such file <" << e.getUri() << ">\n";
625 return EXIT_FAILURE;
627 catch (unoidl::FileFormatException & e)
629 std::cerr
630 << "ERROR: Bad format of <" << e.getUri() << ">, \""
631 << e.getDetail() << "\"\n";
632 return EXIT_FAILURE;
634 catch (Exception & exc)
636 OString msg(
637 OUStringToOString( exc.Message, osl_getThreadTextEncoding() ) );
638 fprintf(
639 stderr, "\n> error: %s\n> dying abnormally...\n", msg.getStr() );
640 ret = 1;
642 catch (::System::Exception ^ exc)
644 OString msg( OUStringToOString(
645 String_to_ustring( exc->ToString() ),
646 osl_getThreadTextEncoding() ) );
647 fprintf(
648 stderr,
649 "\n> error: .NET exception occurred: %s\n> dying abnormally...",
650 msg.getStr() );
651 ret = 1;
656 css::uno::Reference< lang::XComponent > xComp( xContext, UNO_QUERY );
657 if (xComp.is())
658 xComp->dispose();
660 catch (Exception & exc)
662 OString msg(
663 OUStringToOString( exc.Message, osl_getThreadTextEncoding() ) );
664 fprintf(
665 stderr,
666 "\n> error disposing component context: %s\n"
667 "> dying abnormally...\n",
668 msg.getStr() );
669 ret = 1;
672 return ret;
675 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */