~zorba-coders/zorba/project_tag_process

« back to all changes in this revision

Viewing changes to src/process-1.xq.src/process.cpp

  • Committer: Zorba Build Bot
  • Author(s): nbrinza at gmail
  • Date: 2013-06-13 11:27:11 UTC
  • mfrom: (33.2.17 process-2)
  • Revision ID: chillery+buildbot@lambda.nu-20130613112711-4qkacysimbs4kpvl
Version 2.0 of the process module, allows running executables directly, without invoking bash/cmd.exe Approved: Nicolae Brinza, Matthias Brantner

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2006-2008 The FLWOR Foundation.
 
3
 *
 
4
 * Licensed under the Apache License, Version 2.0 (the "License");
 
5
 * you may not use this file except in compliance with the License.
 
6
 * You may obtain a copy of the License at
 
7
 *
 
8
 * http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * Unless required by applicable law or agreed to in writing, software
 
11
 * distributed under the License is distributed on an "AS IS" BASIS,
 
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
 * See the License for the specific language governing permissions and
 
14
 * limitations under the License.
 
15
 */
 
16
 
 
17
#include <sstream>
 
18
#include <stdlib.h>
 
19
#include <stdio.h>
 
20
#include <errno.h>
 
21
#include <vector>
 
22
#include <iostream>
 
23
#include <limits.h>
 
24
#include <algorithm>
 
25
 
 
26
#ifdef WIN32
 
27
#  include <windows.h>
 
28
 
 
29
#  ifndef NDEBUG
 
30
#    define _CRTDBG_MAP_ALLOC
 
31
#    include <stdlib.h>
 
32
#    include <crtdbg.h>
 
33
#   endif
 
34
#else
 
35
#  include <unistd.h>
 
36
#  ifdef __APPLE__
 
37
#    include <sys/wait.h>
 
38
#  else
 
39
#    include <wait.h>
 
40
#  endif
 
41
#endif
 
42
 
 
43
#include <zorba/item_factory.h>
 
44
#include <zorba/singleton_item_sequence.h>
 
45
#include <zorba/diagnostic_list.h>
 
46
#include <zorba/user_exception.h>
 
47
#include <zorba/empty_sequence.h>
 
48
 
 
49
#include "process.h"
 
50
 
 
51
namespace zorba {
 
52
namespace processmodule {
 
53
 
 
54
/******************************************************************************
 
55
 *****************************************************************************/
 
56
void create_result_node(
 
57
    zorba::Item&        aResult,
 
58
    const std::string&  aStandardOut,
 
59
    const std::string&  aErrorOut,
 
60
    int                 aExitCode,
 
61
    zorba::ItemFactory* aFactory)
 
62
{
 
63
  zorba::Item lResultQName =
 
64
    aFactory->createQName("http://www.zorba-xquery.com/modules/process", "result");
 
65
  zorba::Item lExitCodeQName =
 
66
    aFactory->createQName("http://www.zorba-xquery.com/modules/process", "exit-code");
 
67
  zorba::Item lOutputQName =
 
68
    aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stdout");
 
69
  zorba::Item lErrorQName =
 
70
    aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stderr");
 
71
  zorba::Item lNullItem;
 
72
  zorba::Item lTypeName =
 
73
    aFactory->createQName("http://www.w3.org/2001/XMLSchema", "untyped");
 
74
 
 
75
  zorba::NsBindings lNSBindings;
 
76
 
 
77
  // root node called result
 
78
  aResult = aFactory->createElementNode(
 
79
      lNullItem, lResultQName, lTypeName, false, false, lNSBindings);
 
80
 
 
81
  // <result><output> aStandardOut </output></result>
 
82
  zorba::Item lOutput;
 
83
  lOutput = aFactory->createElementNode(
 
84
      aResult, lOutputQName, lTypeName, true, false, lNSBindings);
 
85
  aFactory->createTextNode(lOutput, aStandardOut);
 
86
 
 
87
  // <result><error> aErrorOut </error></result>
 
88
  zorba::Item lError;
 
89
  lError = aFactory->createElementNode(
 
90
      aResult, lErrorQName, lTypeName, true, false, lNSBindings);
 
91
  aFactory->createTextNode(lError, aErrorOut);
 
92
 
 
93
  // <result><exit-code> aExitCode </exit-code></result>
 
94
  zorba::Item lExitCode;
 
95
  lExitCode = aFactory->createElementNode(
 
96
      aResult, lExitCodeQName, lTypeName, true, false, lNSBindings);
 
97
  std::ostringstream lExitCodeString;
 
98
  lExitCodeString << aExitCode;
 
99
  aFactory->createTextNode(lExitCode, lExitCodeString.str());
 
100
}
 
101
 
 
102
#ifdef WIN32
 
103
 
 
104
/***********************************************
 
105
*  throw a descriptive message of the last error
 
106
*  accessible with GetLastError() on windows
 
107
*/
 
108
void throw_last_error(const zorba::String& aFilename, unsigned int aLineNumber){
 
109
  LPVOID lpvMessageBuffer;
 
110
  TCHAR lErrorBuffer[512];
 
111
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
 
112
          NULL, GetLastError(),
 
113
          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 
114
          (LPTSTR)&lpvMessageBuffer, 0, NULL);
 
115
  wsprintf(lErrorBuffer,TEXT("Process Error Code: %d - Message= %s"),GetLastError(), (TCHAR *)lpvMessageBuffer);
 
116
  LocalFree(lpvMessageBuffer);
 
117
  Item lQName = ProcessModule::getItemFactory()->createQName(
 
118
    "http://www.zorba-xquery.com/modules/process",
 
119
    "PROC01");
 
120
#ifdef UNICODE
 
121
  char error_str[1024];
 
122
  WideCharToMultiByte(CP_UTF8, 0, lErrorBuffer, -1, error_str, sizeof(error_str), NULL, NULL);
 
123
  throw USER_EXCEPTION(lQName, error_str);
 
124
#else
 
125
  throw USER_EXCEPTION(lQName, lErrorBuffer);
 
126
#endif
 
127
}
 
128
 
 
129
/******************************************
 
130
*  read output from child process on windows
 
131
*/
 
132
void read_child_output(HANDLE aOutputPipe, std::ostringstream& aTargetStream)
 
133
{
 
134
  CHAR lBuffer[256];
 
135
  DWORD lBytesRead;
 
136
  
 
137
  while(TRUE)
 
138
  {
 
139
    if (
 
140
      !ReadFile(aOutputPipe,lBuffer,sizeof(lBuffer),&lBytesRead,NULL) 
 
141
      || !lBytesRead
 
142
    )
 
143
    {
 
144
      if (GetLastError() == ERROR_BROKEN_PIPE)
 
145
        break; // finished
 
146
      else{
 
147
      
 
148
         // couldn't read from pipe
 
149
         throw_last_error(__FILE__, __LINE__);
 
150
      }
 
151
    }
 
152
    
 
153
    // remove the windows specific carriage return outputs
 
154
   // std::stringstream lTmp;
 
155
   // lTmp.write(lBuffer,lBytesRead);
 
156
   // std::string lRawString=lTmp.str();
 
157
   // std::replace( lRawString.begin(), lRawString.end(), '\r', ' ' );
 
158
   // aTargetStream.write(lRawString.c_str(),static_cast<std::streamsize>(lRawString.length()));
 
159
    for(DWORD i=0;i<lBytesRead;i++)
 
160
    {
 
161
      if(lBuffer[i] != '\r')
 
162
        aTargetStream << lBuffer[i];
 
163
    }
 
164
    lBytesRead = 0;
 
165
  }
 
166
}
 
167
 
 
168
/******************************************
 
169
*  Create a child process on windows with
 
170
*  redirected output
 
171
*/
 
172
BOOL create_child_process(HANDLE aStdOutputPipe,HANDLE aStdErrorPipe,const std::string& aCommand,PROCESS_INFORMATION& aProcessInformation){
 
173
  STARTUPINFO lChildStartupInfo;
 
174
  BOOL result=FALSE;
 
175
  
 
176
  // set the output handles
 
177
  FillMemory(&lChildStartupInfo,sizeof(lChildStartupInfo),0);
 
178
  lChildStartupInfo.cb = sizeof(lChildStartupInfo);
 
179
  GetStartupInfo(&lChildStartupInfo);
 
180
  lChildStartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
 
181
  lChildStartupInfo.wShowWindow = SW_HIDE; // don't show the command window
 
182
  lChildStartupInfo.hStdOutput = aStdOutputPipe;
 
183
  lChildStartupInfo.hStdError  = aStdErrorPipe;
 
184
 
 
185
  // convert from const char* to char*
 
186
  size_t length = strlen(aCommand.c_str());
 
187
#ifdef UNICODE
 
188
  WCHAR *tmpCommand = new WCHAR[length+1];
 
189
  MultiByteToWideChar(CP_UTF8, 0, aCommand.c_str(), -1, tmpCommand, length+1);
 
190
#else
 
191
  char *tmpCommand=new char[length+1];
 
192
  strcpy (tmpCommand,aCommand.c_str());
 
193
  tmpCommand[length]='\0';
 
194
#endif
 
195
 
 
196
  try{
 
197
  
 
198
    // settings for the child process      
 
199
    LPCTSTR lApplicationName=NULL;
 
200
    LPTSTR lCommandLine=tmpCommand;
 
201
    LPSECURITY_ATTRIBUTES lProcessAttributes=NULL;
 
202
    LPSECURITY_ATTRIBUTES lThreadAttributes=NULL;
 
203
    BOOL lInheritHandles=TRUE; // that's what we want
 
204
    DWORD lCreationFlags=CREATE_NEW_CONSOLE;
 
205
    LPVOID lEnvironment=NULL;
 
206
    LPCTSTR lCurrentDirectory=NULL; // same as main process
 
207
    
 
208
    // start child
 
209
    result=CreateProcess(
 
210
          lApplicationName,lCommandLine,lProcessAttributes,
 
211
          lThreadAttributes,lInheritHandles,lCreationFlags,
 
212
          lEnvironment,lCurrentDirectory,&lChildStartupInfo,
 
213
          &aProcessInformation);
 
214
          
 
215
  }catch(...){
 
216
    delete[] tmpCommand;
 
217
    tmpCommand=0;
 
218
    throw;  
 
219
  }
 
220
        
 
221
  delete[] tmpCommand;
 
222
  tmpCommand=0;
 
223
        
 
224
  return result;
 
225
}
 
226
 
 
227
/******************************************
 
228
*  run a process that executes the aCommand
 
229
*  in a new console and reads the output
 
230
*/
 
231
int run_process(
 
232
  const std::string& aCommand,
 
233
  std::ostringstream& aTargetOutStream,
 
234
  std::ostringstream& aTargetErrStream)
 
235
{
 
236
  HANDLE lOutRead, lErrRead, lStdOut, lStdErr;
 
237
  SECURITY_ATTRIBUTES lSecurityAttributes;
 
238
  PROCESS_INFORMATION lChildProcessInfo;
 
239
  DWORD exitCode=0;
 
240
    
 
241
  // prepare security attributes
 
242
  lSecurityAttributes.nLength= sizeof(lSecurityAttributes);
 
243
  lSecurityAttributes.lpSecurityDescriptor = NULL;
 
244
  lSecurityAttributes.bInheritHandle = TRUE;
 
245
 
 
246
  // create output pipes
 
247
  if(
 
248
      !CreatePipe(&lOutRead,&lStdOut,&lSecurityAttributes,1024*1024) // std::cout >> lOutRead
 
249
      || !CreatePipe(&lErrRead,&lStdErr,&lSecurityAttributes,1024*1024) // std::cerr >> lErrRead
 
250
    ){
 
251
    Item lQName = ProcessModule::getItemFactory()->createQName(
 
252
      "http://www.zorba-xquery.com/modules/process", "PROC01");
 
253
    throw USER_EXCEPTION(lQName,
 
254
      "Couldn't create one of std::cout/std::cerr pipe for child process execution."
 
255
    );
 
256
  };
 
257
  
 
258
  //start child process
 
259
  BOOL ok = create_child_process(lStdOut,lStdErr,aCommand,lChildProcessInfo);
 
260
  if(ok==TRUE)
 
261
  {
 
262
 
 
263
    // close unneeded handle  
 
264
    CloseHandle(lChildProcessInfo.hThread);
 
265
    
 
266
    // wait for the process to finish
 
267
    WaitForSingleObject(lChildProcessInfo.hProcess,INFINITE);
 
268
    if (!GetExitCodeProcess(lChildProcessInfo.hProcess, &exitCode))
 
269
    {
 
270
      std::stringstream lErrorMsg;
 
271
      lErrorMsg 
 
272
        << "Couldn't get exit code from child process. Executed command: '" << aCommand << "'.";
 
273
      Item lQName = ProcessModule::getItemFactory()->createQName(
 
274
        "http://www.zorba-xquery.com/modules/process", "PROC01");
 
275
      throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
 
276
    }
 
277
  
 
278
    CloseHandle(lChildProcessInfo.hProcess);
 
279
    CloseHandle(lStdOut);
 
280
    CloseHandle(lStdErr);
 
281
 
 
282
    // read child's output
 
283
    read_child_output(lOutRead,aTargetOutStream);
 
284
    read_child_output(lErrRead,aTargetErrStream);
 
285
 
 
286
    // close 
 
287
    CloseHandle(lOutRead);
 
288
    CloseHandle(lErrRead);
 
289
 
 
290
 }else{
 
291
    CloseHandle(lStdOut);
 
292
    CloseHandle(lStdErr);
 
293
    CloseHandle(lOutRead);
 
294
    CloseHandle(lErrRead);
 
295
  
 
296
     // couldn't launch process
 
297
     throw_last_error(__FILE__, __LINE__);
 
298
  };
 
299
  
 
300
  
 
301
  return exitCode;
 
302
}
 
303
 
 
304
#else
 
305
 
 
306
#define READ  0
 
307
#define WRITE 1
 
308
 
 
309
pid_t zorba_popen(const char *command, int *infp, int *outfp, int *errfp)
 
310
{
 
311
    int p_stdin[2];
 
312
    int p_stdout[2];
 
313
    int p_stderr[2];
 
314
    pid_t pid;
 
315
 
 
316
    if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0)
 
317
      return -1;
 
318
 
 
319
    pid = fork();
 
320
 
 
321
    if (pid < 0)
 
322
      return pid;
 
323
    else if (pid == 0)
 
324
    {
 
325
      close(p_stdin[WRITE]);
 
326
      dup2(p_stdin[READ], 0);   // duplicate stdin
 
327
 
 
328
      close(p_stdout[READ]);
 
329
      dup2(p_stdout[WRITE], 1); // duplicate stdout
 
330
 
 
331
      close(p_stderr[READ]);
 
332
      dup2(p_stderr[WRITE], 2); // duplicate stderr
 
333
 
 
334
      execl("/bin/sh", "sh", "-c", command, NULL);
 
335
      perror("execl"); // output the result to standard error
 
336
      exit(errno);
 
337
    }
 
338
 
 
339
    if (infp == NULL)
 
340
      close(p_stdin[WRITE]);
 
341
    else
 
342
      *infp = p_stdin[WRITE];
 
343
 
 
344
    if (outfp == NULL)
 
345
      close(p_stdout[READ]);
 
346
    else
 
347
      *outfp = p_stdout[READ];
 
348
 
 
349
    if (errfp == NULL)
 
350
      close(p_stderr[READ]);
 
351
    else
 
352
      *errfp = p_stderr[READ];
 
353
 
 
354
    close(p_stdin[READ]);   // We only write to the forks stdin anyway
 
355
    close(p_stdout[WRITE]); // and we only read from its stdout
 
356
    close(p_stderr[WRITE]); // and we only read from its stderr
 
357
        
 
358
    return pid;
 
359
}
 
360
#endif
 
361
 
 
362
/******************************************************************************
 
363
 *****************************************************************************/
 
364
zorba::ItemSequence_t
 
365
ExecFunction::evaluate(
 
366
  const Arguments_t& aArgs,
 
367
  const zorba::StaticContext* aSctx,
 
368
  const zorba::DynamicContext* aDctx) const
 
369
{
 
370
  std::string lCommand;
 
371
  std::vector<std::string> lArgs;
 
372
  int exit_code = 0;
 
373
 
 
374
  lCommand = getOneStringArgument(aArgs, 0).c_str();
 
375
 
 
376
  if (aArgs.size() > 1)
 
377
  {
 
378
    zorba::Item lArg;
 
379
    Iterator_t arg1_iter = aArgs[1]->getIterator();
 
380
    arg1_iter->open();
 
381
    while (arg1_iter->next(lArg))
 
382
    {
 
383
      lArgs.push_back(lArg.getStringValue().c_str());
 
384
    }
 
385
    arg1_iter->close();
 
386
  }
 
387
 
 
388
  std::ostringstream lTmp;
 
389
 
 
390
#ifdef WIN32
 
391
  // execute process command in a new commandline
 
392
  // with quotes at the beggining and at the end
 
393
  lTmp << "cmd /C \"";
 
394
#endif
 
395
 
 
396
  lTmp << "\"" << lCommand << "\""; //quoted for spaced paths/filenames
 
397
  size_t pos=0;
 
398
  for (std::vector<std::string>::const_iterator lIter = lArgs.begin();
 
399
       lIter != lArgs.end(); ++lIter)
 
400
  {
 
401
    pos = (*lIter).rfind('\\')+(*lIter).rfind('/');
 
402
    if (int(pos)>=0)
 
403
      lTmp << " \"" << *lIter << "\"";
 
404
    else
 
405
      lTmp << " " << *lIter;
 
406
  }
 
407
#ifdef WIN32
 
408
  lTmp << "\"";   // with quotes at the end for commandline
 
409
#endif
 
410
  
 
411
  std::ostringstream lStdout;
 
412
  std::ostringstream lStderr;
 
413
 
 
414
#ifdef WIN32
 
415
  std::string lCommandLineString = lTmp.str();
 
416
  int code = run_process(lCommandLineString, lStdout, lStderr);
 
417
  
 
418
  if (code != 0)
 
419
  {
 
420
    std::stringstream lErrorMsg;
 
421
    lErrorMsg << "Failed to execute the command (" << code << ")";
 
422
    Item lQName = ProcessModule::getItemFactory()->createQName(
 
423
      "http://www.zorba-xquery.com/modules/process", "PROC01");
 
424
    throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
 
425
  }
 
426
  exit_code = code;
 
427
 
 
428
#else //not WIN32
 
429
 
 
430
  int outfp;
 
431
  int errfp;
 
432
  int status;
 
433
  pid_t pid;
 
434
 
 
435
  pid = zorba_popen(lTmp.str().c_str(), NULL, &outfp, &errfp);
 
436
  if ( pid == -1 )
 
437
  {
 
438
    std::stringstream lErrorMsg;
 
439
    lErrorMsg << "Failed to execute the command (" << pid << ")";
 
440
    Item lQName = ProcessModule::getItemFactory()->createQName(
 
441
      "http://www.zorba-xquery.com/modules/process", "PROC01");
 
442
    throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
 
443
  }
 
444
  else
 
445
  {
 
446
    char lBuf[PATH_MAX];
 
447
    ssize_t length = 0;
 
448
    while ( (length=read(outfp, lBuf, PATH_MAX)) > 0 )
 
449
    {
 
450
      lStdout.write(lBuf, length);
 
451
    }
 
452
 
 
453
    status = close(outfp);
 
454
 
 
455
    while ( (length=read(errfp, lBuf, PATH_MAX)) > 0 )
 
456
    {
 
457
      lStderr.write(lBuf, length);
 
458
    }
 
459
 
 
460
    status = close(errfp);
 
461
 
 
462
    if ( status < 0 )
 
463
    {
 
464
      std::stringstream lErrorMsg;
 
465
      lErrorMsg << "Failed to close the err stream (" << status << ")";
 
466
      Item lQName = ProcessModule::getItemFactory()->createQName(
 
467
        "http://www.zorba-xquery.com/modules/process", "PROC01");
 
468
      throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
 
469
    }
 
470
 
 
471
    int  stat = 0;
 
472
    
 
473
    pid_t w = waitpid(pid, &stat, 0);
 
474
    
 
475
    if (w == -1) 
 
476
    { 
 
477
        std::stringstream lErrorMsg;
 
478
        lErrorMsg << "Failed to wait for child process ";
 
479
        Item lQName = ProcessModule::getItemFactory()->createQName(
 
480
          "http://www.zorba-xquery.com/modules/process", "PROC01");
 
481
        throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());          
 
482
    }
 
483
 
 
484
    if (WIFEXITED(stat)) 
 
485
    {
 
486
        //std::cout << " WEXITSTATUS : " << WEXITSTATUS(stat) << std::endl; std::cout.flush();
 
487
        exit_code = WEXITSTATUS(stat);
 
488
    } 
 
489
    else if (WIFSIGNALED(stat)) 
 
490
    {
 
491
        //std::cout << " WTERMSIG : " << WTERMSIG(stat) << std::endl; std::cout.flush();
 
492
        exit_code = 128 + WTERMSIG(stat);
 
493
    }
 
494
    else if (WIFSTOPPED(stat)) 
 
495
    {
 
496
        //std::cout << " STOPSIG : " << WSTOPSIG(stat) << std::endl; std::cout.flush();
 
497
        exit_code = 128 + WSTOPSIG(stat);
 
498
    }
 
499
    else
 
500
    {
 
501
        //std::cout << " else : " << std::endl; std::cout.flush();
 
502
        exit_code = 255;
 
503
    }
 
504
    
 
505
    //std::cout << " exit_code : " << exit_code << std::endl; std::cout.flush();
 
506
 
 
507
  }
 
508
#endif // WIN32
 
509
 
 
510
  zorba::Item lResult;
 
511
  create_result_node(lResult, lStdout.str(), lStderr.str(), exit_code,
 
512
                     theModule->getItemFactory());
 
513
 
 
514
  return zorba::ItemSequence_t(new zorba::SingletonItemSequence(lResult));
 
515
}
 
516
 
 
517
String ExecFunction::getOneStringArgument (const Arguments_t& aArgs, int aPos)
 
518
  const
 
519
{
 
520
  Item lItem;
 
521
  Iterator_t  args_iter = aArgs[aPos]->getIterator();
 
522
  args_iter->open();
 
523
  args_iter->next(lItem);
 
524
  zorba::String lTmpString = lItem.getStringValue();
 
525
  args_iter->close();
 
526
  return lTmpString;
 
527
}
 
528
 
 
529
/******************************************************************************
 
530
 *****************************************************************************/
 
531
ProcessModule::~ProcessModule()
 
532
{
 
533
  for (FuncMap_t::const_iterator lIter = theFunctions.begin();
 
534
       lIter != theFunctions.end(); ++lIter) {
 
535
    delete lIter->second;
 
536
  }
 
537
  theFunctions.clear();
 
538
}
 
539
 
 
540
zorba::ExternalFunction*
 
541
ProcessModule::getExternalFunction(const zorba::String& aLocalname)
 
542
{
 
543
  FuncMap_t::const_iterator lFind = theFunctions.find(aLocalname);
 
544
  zorba::ExternalFunction*& lFunc = theFunctions[aLocalname];
 
545
  if (lFind == theFunctions.end())
 
546
  {
 
547
    if (!aLocalname.compare("exec"))
 
548
    {
 
549
      lFunc = new ExecFunction(this);
 
550
    }
 
551
  }
 
552
  return lFunc;
 
553
}
 
554
 
 
555
void ProcessModule::destroy()
 
556
{
 
557
  if (!dynamic_cast<ProcessModule*>(this)) {
 
558
    return;
 
559
  }
 
560
  delete this;
 
561
}
 
562
 
 
563
ItemFactory* ProcessModule::theFactory = 0;
 
564
 
 
565
} /* namespace processmodule */
 
566
} /* namespace zorba */
 
567
 
 
568
#ifdef WIN32
 
569
#  define DLL_EXPORT __declspec(dllexport)
 
570
#else
 
571
#  define DLL_EXPORT __attribute__ ((visibility("default")))
 
572
#endif
 
573
 
 
574
extern "C" DLL_EXPORT zorba::ExternalModule* createModule() {
 
575
  return new zorba::processmodule::ProcessModule();
 
576
}
 
577
/* vim:set et sw=2 ts=2: */