summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2022-07-22 14:14:18 (GMT)
committerKitware Robot <kwrobot@kitware.com>2022-07-22 14:14:34 (GMT)
commit66998035c1d63df3c98ca42cb937aecf1574da8d (patch)
treee5701ab9fc4a2aaf73c8d349bf442ebd61e003de
parent1cee9a9012ac8915c168d20feae2d33cafd75605 (diff)
parenta2cd0687db1afec58d5f42a94bb85b2e2ce301fe (diff)
downloadCMake-66998035c1d63df3c98ca42cb937aecf1574da8d.zip
CMake-66998035c1d63df3c98ca42cb937aecf1574da8d.tar.gz
CMake-66998035c1d63df3c98ca42cb937aecf1574da8d.tar.bz2
Merge topic 'try_run_split_output'
a2cd0687db try_run: Add RUN_OUTPUT_STDOUT_VARIABLE and RUN_OUTPUT_STDERR_VARIABLE. Acked-by: Kitware Robot <kwrobot@kitware.com> Acked-by: buildbot <buildbot@kitware.com> Merge-request: !7453
-rw-r--r--Help/command/try_run.rst13
-rw-r--r--Help/release/dev/try_run_split_output.rst6
-rw-r--r--Source/cmTryRunCommand.cxx171
-rw-r--r--Source/cmTryRunCommand.h10
-rw-r--r--Tests/RunCMake/try_run/BadStdErrVariable-result.txt1
-rw-r--r--Tests/RunCMake/try_run/BadStdErrVariable-stderr.txt5
-rw-r--r--Tests/RunCMake/try_run/BadStdErrVariable.cmake5
-rw-r--r--Tests/RunCMake/try_run/BadStdOutVariable-result.txt1
-rw-r--r--Tests/RunCMake/try_run/BadStdOutVariable-stderr.txt5
-rw-r--r--Tests/RunCMake/try_run/BadStdOutVariable.cmake5
-rw-r--r--Tests/RunCMake/try_run/RunCMakeTest.cmake3
-rw-r--r--Tests/TryCompile/CMakeLists.txt23
-rw-r--r--Tests/TryCompile/stdout_and_stderr.c8
13 files changed, 241 insertions, 15 deletions
diff --git a/Help/command/try_run.rst b/Help/command/try_run.rst
index fc41cdd..125b0ac 100644
--- a/Help/command/try_run.rst
+++ b/Help/command/try_run.rst
@@ -19,6 +19,8 @@ Try Compiling and Running Source Files
[LINK_LIBRARIES <libs>...]
[COMPILE_OUTPUT_VARIABLE <var>]
[RUN_OUTPUT_VARIABLE <var>]
+ [RUN_OUTPUT_STDOUT_VARIABLE <var>]
+ [RUN_OUTPUT_STDERR_VARIABLE <var>]
[OUTPUT_VARIABLE <var>]
[WORKING_DIRECTORY <var>]
[ARGS <args>...])
@@ -70,6 +72,16 @@ The options are:
``RUN_OUTPUT_VARIABLE <var>``
Report the output from running the executable in a given variable.
+``RUN_OUTPUT_STDOUT_VARIABLE <var>``
+ .. versionadded:: 3.25
+
+ Report the output of stdout from running the executable in a given variable.
+
+``RUN_OUTPUT_STDERR_VARIABLE <var>``
+ .. versionadded:: 3.25
+
+ Report the output of stderr from running the executable in a given variable.
+
``WORKING_DIRECTORY <var>``
.. versionadded:: 3.20
@@ -110,6 +122,7 @@ These cache entries are:
In order to make cross compiling your project easier, use ``try_run``
only if really required. If you use ``try_run``, use the
+``RUN_OUTPUT_STDOUT_VARIABLE``, ``RUN_OUTPUT_STDERR_VARIABLE``,
``RUN_OUTPUT_VARIABLE`` or ``OUTPUT_VARIABLE`` options only if really
required. Using them will require that when cross-compiling, the cache
variables will have to be set manually to the output of the executable.
diff --git a/Help/release/dev/try_run_split_output.rst b/Help/release/dev/try_run_split_output.rst
new file mode 100644
index 0000000..98aedd6
--- /dev/null
+++ b/Help/release/dev/try_run_split_output.rst
@@ -0,0 +1,6 @@
+try_run_split_output
+--------------------
+
+* The :command:`try_run` command gained ``RUN_OUTPUT_STDOUT_VARIABLE``
+ and ``RUN_OUTPUT_STDERR_VARIABLE`` options to capture stdout and stderr
+ separately from the output of the compiled program.
diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx
index c82ac64..4cd0adc 100644
--- a/Source/cmTryRunCommand.cxx
+++ b/Source/cmTryRunCommand.cxx
@@ -42,6 +42,8 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
this->RunResultVariable.clear();
this->OutputVariable.clear();
this->RunOutputVariable.clear();
+ this->RunOutputStdOutVariable.clear();
+ this->RunOutputStdErrVariable.clear();
this->CompileOutputVariable.clear();
std::string runArgs;
@@ -76,6 +78,22 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
}
i++;
this->RunOutputVariable = argv[i];
+ } else if (argv[i] == "RUN_OUTPUT_STDOUT_VARIABLE") {
+ if (argv.size() <= (i + 1)) {
+ cmSystemTools::Error(
+ "RUN_OUTPUT_STDOUT_VARIABLE specified but there is no variable");
+ return false;
+ }
+ i++;
+ this->RunOutputStdOutVariable = argv[i];
+ } else if (argv[i] == "RUN_OUTPUT_STDERR_VARIABLE") {
+ if (argv.size() <= (i + 1)) {
+ cmSystemTools::Error(
+ "RUN_OUTPUT_STDERR_VARIABLE specified but there is no variable");
+ return false;
+ }
+ i++;
+ this->RunOutputStdErrVariable = argv[i];
} else if (argv[i] == "COMPILE_OUTPUT_VARIABLE") {
if (argv.size() <= (i + 1)) {
cmSystemTools::Error(
@@ -102,11 +120,27 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
// using OUTPUT_VARIABLE makes crosscompiling harder
if (!this->OutputVariable.empty() &&
(!this->RunOutputVariable.empty() ||
- !this->CompileOutputVariable.empty())) {
+ !this->CompileOutputVariable.empty() ||
+ !this->RunOutputStdOutVariable.empty() ||
+ !this->RunOutputStdErrVariable.empty())) {
cmSystemTools::Error(
"You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE "
- "or RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE and/or "
- "RUN_OUTPUT_VARIABLE.");
+ ", RUN_OUTPUT_VARIABLE, RUN_OUTPUT_STDOUT_VARIABLE or "
+ "RUN_OUTPUT_STDERR_VARIABLE. "
+ "Please use only COMPILE_OUTPUT_VARIABLE, RUN_OUTPUT_VARIABLE, "
+ "RUN_OUTPUT_STDOUT_VARIABLE "
+ "and/or RUN_OUTPUT_STDERR_VARIABLE.");
+ return false;
+ }
+
+ if ((!this->RunOutputStdOutVariable.empty() ||
+ !RunOutputStdErrVariable.empty()) &&
+ !this->RunOutputVariable.empty()) {
+ cmSystemTools::Error(
+ "You cannot use RUN_OUTPUT_STDOUT_VARIABLE or "
+ "RUN_OUTPUT_STDERR_VARIABLE together "
+ "with RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE or "
+ "RUN_OUTPUT_STDOUT_VARIABLE and/or RUN_OUTPUT_STDERR_VARIABLE.");
return false;
}
@@ -119,6 +153,7 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
}
bool captureRunOutput = false;
+ bool captureRunOutputStdOutErr = false;
if (!this->OutputVariable.empty()) {
captureRunOutput = true;
tryCompile.emplace_back("OUTPUT_VARIABLE");
@@ -128,7 +163,10 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
tryCompile.emplace_back("OUTPUT_VARIABLE");
tryCompile.push_back(this->CompileOutputVariable);
}
- if (!this->RunOutputVariable.empty()) {
+ if (!this->RunOutputStdOutVariable.empty() ||
+ !RunOutputStdErrVariable.empty()) {
+ captureRunOutputStdOutErr = true;
+ } else if (!this->RunOutputVariable.empty()) {
captureRunOutput = true;
}
@@ -145,12 +183,27 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
} else {
// "run" it and capture the output
std::string runOutputContents;
+ std::string runOutputStdOutContents;
+ std::string runOutputStdErrContents;
if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING") &&
!this->Makefile->IsDefinitionSet("CMAKE_CROSSCOMPILING_EMULATOR")) {
this->DoNotRunExecutable(
- runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr);
+ runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr,
+ captureRunOutputStdOutErr && !RunOutputStdOutVariable.empty()
+ ? &runOutputStdOutContents
+ : nullptr,
+ captureRunOutputStdOutErr && !RunOutputStdErrVariable.empty()
+ ? &runOutputStdErrContents
+ : nullptr);
} else {
- this->RunExecutable(runArgs, &runOutputContents);
+ this->RunExecutable(
+ runArgs, captureRunOutput ? &runOutputContents : nullptr,
+ captureRunOutputStdOutErr && !RunOutputStdOutVariable.empty()
+ ? &runOutputStdOutContents
+ : nullptr,
+ captureRunOutputStdOutErr && !RunOutputStdErrVariable.empty()
+ ? &runOutputStdErrContents
+ : nullptr);
}
// now put the output into the variables
@@ -158,6 +211,14 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
this->Makefile->AddDefinition(this->RunOutputVariable,
runOutputContents);
}
+ if (!this->RunOutputStdOutVariable.empty()) {
+ this->Makefile->AddDefinition(this->RunOutputStdOutVariable,
+ runOutputStdOutContents);
+ }
+ if (!this->RunOutputStdErrVariable.empty()) {
+ this->Makefile->AddDefinition(this->RunOutputStdErrVariable,
+ runOutputStdErrContents);
+ }
if (!this->OutputVariable.empty()) {
// if the TryCompileCore saved output in this outputVariable then
@@ -180,7 +241,8 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
}
void cmTryRunCommand::RunExecutable(const std::string& runArgs,
- std::string* out)
+ std::string* out, std::string* stdOut,
+ std::string* stdErr)
{
int retVal = -1;
@@ -204,7 +266,8 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs,
finalCommand += runArgs;
}
bool worked = cmSystemTools::RunSingleCommand(
- finalCommand, out, out, &retVal,
+ finalCommand, stdOut || stdErr ? stdOut : out,
+ stdOut || stdErr ? stdErr : out, &retVal,
this->WorkingDirectory.empty() ? nullptr : this->WorkingDirectory.c_str(),
cmSystemTools::OUTPUT_NONE, cmDuration::zero());
// set the run var
@@ -227,7 +290,8 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs,
*/
void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
const std::string& srcFile,
- std::string* out)
+ std::string* out, std::string* stdOut,
+ std::string* stdErr)
{
// copy the executable out of the CMakeFiles/ directory, so it is not
// removed at the end of try_run() and the user can run it manually
@@ -246,6 +310,10 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
std::string internalRunOutputName =
this->RunResultVariable + "__TRYRUN_OUTPUT";
+ std::string internalRunOutputStdOutName =
+ this->RunResultVariable + "__TRYRUN_OUTPUT_STDOUT";
+ std::string internalRunOutputStdErrName =
+ this->RunResultVariable + "__TRYRUN_OUTPUT_STDERR";
bool error = false;
if (!this->Makefile->GetDefinition(this->RunResultVariable)) {
@@ -269,7 +337,51 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
}
// is the output from the executable used ?
- if (out) {
+ if (stdOut || stdErr) {
+ if (!this->Makefile->GetDefinition(internalRunOutputStdOutName)) {
+ // if the variables doesn't exist, create it with a helpful error text
+ // and mark it as advanced
+ std::string comment = cmStrCat(
+ "Output of try_run(), contains the text, which the executable "
+ "would have printed on stdout on its target platform.\n",
+ detailsString);
+
+ this->Makefile->AddCacheDefinition(
+ internalRunOutputStdOutName, "PLEASE_FILL_OUT-NOTFOUND",
+ comment.c_str(), cmStateEnums::STRING);
+ cmState* state = this->Makefile->GetState();
+ cmValue existing =
+ state->GetCacheEntryValue(internalRunOutputStdOutName);
+ if (existing) {
+ state->SetCacheEntryProperty(internalRunOutputStdOutName, "ADVANCED",
+ "1");
+ }
+
+ error = true;
+ }
+
+ if (!this->Makefile->GetDefinition(internalRunOutputStdErrName)) {
+ // if the variables doesn't exist, create it with a helpful error text
+ // and mark it as advanced
+ std::string comment = cmStrCat(
+ "Output of try_run(), contains the text, which the executable "
+ "would have printed on stderr on its target platform.\n",
+ detailsString);
+
+ this->Makefile->AddCacheDefinition(
+ internalRunOutputStdErrName, "PLEASE_FILL_OUT-NOTFOUND",
+ comment.c_str(), cmStateEnums::STRING);
+ cmState* state = this->Makefile->GetState();
+ cmValue existing =
+ state->GetCacheEntryValue(internalRunOutputStdErrName);
+ if (existing) {
+ state->SetCacheEntryProperty(internalRunOutputStdErrName, "ADVANCED",
+ "1");
+ }
+
+ error = true;
+ }
+ } else if (out) {
if (!this->Makefile->GetDefinition(internalRunOutputName)) {
// if the variables doesn't exist, create it with a helpful error text
// and mark it as advanced
@@ -317,7 +429,34 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
" to\n"
" the exit code (in many cases 0 for success), otherwise "
"enter \"FAILED_TO_RUN\".\n");
- if (out) {
+ if (stdOut || stdErr) {
+ if (stdOut) {
+ comment += internalRunOutputStdOutName;
+ comment +=
+ "\n contains the text the executable "
+ "would have printed on stdout.\n"
+ " If the executable would not have been able to run, set ";
+ comment += internalRunOutputStdOutName;
+ comment += " empty.\n"
+ " Otherwise check if the output is evaluated by the "
+ "calling CMake code. If so,\n"
+ " check what the source file would have printed when "
+ "called with the given arguments.\n";
+ }
+ if (stdErr) {
+ comment += internalRunOutputStdErrName;
+ comment +=
+ "\n contains the text the executable "
+ "would have printed on stderr.\n"
+ " If the executable would not have been able to run, set ";
+ comment += internalRunOutputStdErrName;
+ comment += " empty.\n"
+ " Otherwise check if the output is evaluated by the "
+ "calling CMake code. If so,\n"
+ " check what the source file would have printed when "
+ "called with the given arguments.\n";
+ }
+ } else if (out) {
comment += internalRunOutputName;
comment +=
"\n contains the text the executable "
@@ -330,6 +469,7 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
" check what the source file would have printed when "
"called with the given arguments.\n";
}
+
comment += "The ";
comment += this->CompileResultVariable;
comment += " variable holds the build result for this try_run().\n\n"
@@ -370,7 +510,14 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
return;
}
- if (out) {
+ if (stdOut || stdErr) {
+ if (stdOut) {
+ (*stdOut) = *this->Makefile->GetDefinition(internalRunOutputStdOutName);
+ }
+ if (stdErr) {
+ (*stdErr) = *this->Makefile->GetDefinition(internalRunOutputStdErrName);
+ }
+ } else if (out) {
(*out) = *this->Makefile->GetDefinition(internalRunOutputName);
}
}
diff --git a/Source/cmTryRunCommand.h b/Source/cmTryRunCommand.h
index d45acd8..ccf678e 100644
--- a/Source/cmTryRunCommand.h
+++ b/Source/cmTryRunCommand.h
@@ -39,15 +39,21 @@ public:
private:
void RunExecutable(const std::string& runArgs,
- std::string* runOutputContents);
+ std::string* runOutputContents,
+ std::string* runOutputStdOutContents,
+ std::string* runOutputStdErrContents);
void DoNotRunExecutable(const std::string& runArgs,
const std::string& srcFile,
- std::string* runOutputContents);
+ std::string* runOutputContents,
+ std::string* runOutputStdOutContents,
+ std::string* runOutputStdErrContents);
std::string CompileResultVariable;
std::string RunResultVariable;
std::string OutputVariable;
std::string RunOutputVariable;
+ std::string RunOutputStdOutVariable;
+ std::string RunOutputStdErrVariable;
std::string CompileOutputVariable;
std::string WorkingDirectory;
};
diff --git a/Tests/RunCMake/try_run/BadStdErrVariable-result.txt b/Tests/RunCMake/try_run/BadStdErrVariable-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdErrVariable-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/BadStdErrVariable-stderr.txt b/Tests/RunCMake/try_run/BadStdErrVariable-stderr.txt
new file mode 100644
index 0000000..5d7e5e9
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdErrVariable-stderr.txt
@@ -0,0 +1,5 @@
+CMake Error: RUN_OUTPUT_STDERR_VARIABLE specified but there is no variable
+CMake Error at BadStdErrVariable.cmake:1 \(try_run\):
+ try_run unknown error.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_run/BadStdErrVariable.cmake b/Tests/RunCMake/try_run/BadStdErrVariable.cmake
new file mode 100644
index 0000000..88c2a72
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdErrVariable.cmake
@@ -0,0 +1,5 @@
+try_run(RUN_RESULT COMPILE_RESULT
+ ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp/workdir
+ RUN_OUTPUT_STDERR_VARIABLE
+ )
diff --git a/Tests/RunCMake/try_run/BadStdOutVariable-result.txt b/Tests/RunCMake/try_run/BadStdOutVariable-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdOutVariable-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/BadStdOutVariable-stderr.txt b/Tests/RunCMake/try_run/BadStdOutVariable-stderr.txt
new file mode 100644
index 0000000..df60658
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdOutVariable-stderr.txt
@@ -0,0 +1,5 @@
+CMake Error: RUN_OUTPUT_STDOUT_VARIABLE specified but there is no variable
+CMake Error at BadStdOutVariable.cmake:1 \(try_run\):
+ try_run unknown error.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_run/BadStdOutVariable.cmake b/Tests/RunCMake/try_run/BadStdOutVariable.cmake
new file mode 100644
index 0000000..691e881
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdOutVariable.cmake
@@ -0,0 +1,5 @@
+try_run(RUN_RESULT COMPILE_RESULT
+ ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp/workdir
+ RUN_OUTPUT_STDOUT_VARIABLE
+ )
diff --git a/Tests/RunCMake/try_run/RunCMakeTest.cmake b/Tests/RunCMake/try_run/RunCMakeTest.cmake
index d74add0..5fa5b2f 100644
--- a/Tests/RunCMake/try_run/RunCMakeTest.cmake
+++ b/Tests/RunCMake/try_run/RunCMakeTest.cmake
@@ -10,3 +10,6 @@ if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND
endif()
run_cmake(WorkingDirArg)
+
+run_cmake(BadStdOutVariable)
+run_cmake(BadStdErrVariable)
diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt
index 000fd2c..7c6f970 100644
--- a/Tests/TryCompile/CMakeLists.txt
+++ b/Tests/TryCompile/CMakeLists.txt
@@ -259,11 +259,32 @@ endif()
if("${COMPILE_OUTPUT}" MATCHES "hello world")
message(SEND_ERROR " COMPILE_OUT contains the run output: \"${COMPILE_OUTPUT}\"")
endif()
-# check the run output, it should stdout
+# check the run output, it should contain stdout
if(NOT "${RUN_OUTPUT}" MATCHES "hello world")
message(SEND_ERROR " RUN_OUTPUT didn't contain \"hello world\": \"${RUN_OUTPUT}\"")
endif()
+# try to run a file and parse stdout and stderr separately
+try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE
+ ${TryCompile_BINARY_DIR}
+ ${TryCompile_SOURCE_DIR}/stdout_and_stderr.c
+ COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT
+ RUN_OUTPUT_STDOUT_VARIABLE RUN_OUTPUT_STDOUT
+ RUN_OUTPUT_STDERR_VARIABLE RUN_OUTPUT_STDERR)
+
+if(NOT SHOULD_COMPILE)
+ message(STATUS " exit_with_error failed compiling: ${COMPILE_OUTPUT}")
+endif()
+
+# check the run stdout output
+if(NOT "${RUN_OUTPUT_STDOUT}" MATCHES "hello world")
+ message(SEND_ERROR " RUN_OUTPUT_STDOUT didn't contain \"hello world\": \"${RUN_OUTPUT_STDOUT}\"")
+endif()
+# check the run stderr output
+if(NOT "${RUN_OUTPUT_STDERR}" MATCHES "error")
+ message(SEND_ERROR " RUN_OUTPUT_STDERR didn't contain \"error\": \"${RUN_OUTPUT_STDERR}\"")
+endif()
+
#######################################################################
#
# also test that the CHECK_C_SOURCE_COMPILES, CHECK_CXX_SOURCE_COMPILES
diff --git a/Tests/TryCompile/stdout_and_stderr.c b/Tests/TryCompile/stdout_and_stderr.c
new file mode 100644
index 0000000..84ded1f
--- /dev/null
+++ b/Tests/TryCompile/stdout_and_stderr.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int main()
+{
+ fputs("error\n", stderr);
+ puts("hello world\n");
+ return 0;
+}