Repository: conan-io/conan

Issue:
[question] Getting 'settings.compiler.libcxx' value not defined in a --pure-c project
<!-- What is your question? Please be as specific as possible! -->

- [X] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).

Even though I've created a new package with the flag `--pure-c`, which conanfile.py template configures the deletion of the field `self.settings.compiler.libcxx`, the command conan create still fails with the error `ERROR: : 'settings.compiler.libcxx' value not defined`.

Am I doing something wrong? Is that a bug?

Here are the commands:

```
conan new myproj/1.2.3 --test --pure-c
```

```
# generated conanfile.py
    def configure(self):
            del self.settings.compiler.libcxx
```

```
conan create . user/testing -s compiler=clang -s compiler.version=10
Exporting package recipe
myproj/1.2.3@user/testing: A new conanfile.py version was exported
myproj/1.2.3@user/testing: Folder: /home/myuser/.conan/data/myproj/1.2.3/user/testing/export
myproj/1.2.3@user/testing: Exported revision: 71b84694e77a8c54b51a1f797a31116a
Configuration:
[settings]
arch=x86_64
arch_build=x86_64
build_type=Release
compiler=clang
compiler.version=10
os=Linux
os_build=Linux
[options]
[build_requires]
[env]

ERROR: : 'settings.compiler.libcxx' value not defined
```


Discussion:
Hi @hbobenicio 

The issue is that the ``conan new -t`` command is generating a ``test_package`` that is using C++ (check test_package/example.cpp). Then the ``settings.compiler.libcxx`` value is still necessary for building the ``test_package``. You have two options:

- Pass ``-s compiler.libcxx=libstdc++`` in the command line, to specify the missing value for the test_package to know how to build. You can also create your own profile (recommended) and define your settings in profiles files

- Modify the ``test_package`` code to contain only an ``example.c`` pure C example, and add the same ``configure()`` method with the same ``del self.settings.compiler.libcx`` in the ``test_package/conanfile.py`` file.

Note: we might want to provide a test_package that is pure C too, I am labeling this as a UX improvement.
@memsharded Thanks for the quick explanation!

I got it! 

How about when using -t and -c flag at the same time, the `test_package/conanfile.py` template could already also have the configure method deleting it the same way it does on the projects conanfile.py? Sounds a reasonable feature request?
> How about when using -t and -c flag at the same time, the test_package/conanfile.py template could already also have the configure method deleting it the same way it does on the projects conanfile.py? Sounds a reasonable feature request?

Yes, totally, that was what I was thinking when I said:

> we might want to provide a test_package that is pure C too, I am labeling this as a UX improvement.

This would be an easy feature, if you want to try to contribute it yourself 😄 


@memsharded I think I can do that ... ;P  newbie conan user here, so I gotta take a closer look to the code to see how project templates are used based on the CLI comands/flags. My free time is kind of rare, but let's see what I can do... Should we keep it going with this issue or should we open another with as a feature request instead?
This issue is good, no need for a new one.

Of course, no pressure, don't worry if you can't do it, someone else will manage. The code is python I hope you know the basics.

If you want to give it a try, the idea is to put new templates in https://github.com/conan-io/conan/blob/develop/conans/client/cmd/new.py, and then add an extra conditional under ``if test:``

Don't hesitate to ask for guidance or help. Thanks!
Hi @memsharded , me again :)

finally got some time to take a look at it. I see that the file is already importing jinja. Is there any reason that those template strings are not jinja templates? The code is using format a lot and I thought if they were all just jinja template things could be easier to manage probably...

I want not that this would become a lot of strings templating refactoring though as I can't see much further how could this impact the rest of the system. What do you think? Should I try to change those to jinja templates?

We could have blocks just like these:

conanfile in jinja template:
```
    {% if pure_c %}
    def configure(self):
        del self.settings.compiler.libcxx
    {% endif %}
```

test_conanfile:
```
    {% if pure_c %}
    def configure(self):
        del self.settings.compiler.libcxx
    {% endif %}
```

instead of this:
```
        if pure_c:
            config = "    def configure(self):\n        del self.settings.compiler.libcxx\n"
            files["conanfile.py"] = files["conanfile.py"] + config
```
Hello, is @hbobenicio still interested in working on this? Or I can take it over?
H @hbobenicio @sdmg15 

Sorry for not responding @hbobenicio earlier. We got tons of notifications and sometimes they are buried in the backlog.

I would suggest, for whoever will do this, that separate the feature from the refactor, then do 2 Pull Request:

- One PR to move to jinja2 templates
- Another PR for the "C only" feature.

They could be in any order, not critical.
Sorry guys. I've got really busy on work and on personal things so I couldn't follow this up.

@memsharded thanks for the feedback. agreed.

@sdmg15 feel free to handle it. Thanks!
Thanks for the follow up @hbobenicio !

Good, assigned to you @sdmg15 

Resolving patch:
diff --git a/conans/client/cmd/new.py b/conans/client/cmd/new.py
--- a/conans/client/cmd/new.py
+++ b/conans/client/cmd/new.py
@@ -191,6 +191,22 @@ def test(self):
 #          COMMAND example)
 """
 
+test_cmake_pure_c = """cmake_minimum_required(VERSION 2.8.12)
+project(PackageTest C)
+
+include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+conan_basic_setup()
+
+add_executable(example example.c)
+target_link_libraries(example ${CONAN_LIBS})
+
+# CTest is a testing tool that can be used to test your project.
+# enable_testing()
+# add_test(NAME example
+#          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin
+#          COMMAND example)
+"""
+
 test_main = """#include <iostream>
 #include "hello.h"
 
@@ -199,6 +215,26 @@ def test(self):
 }
 """
 
+test_main_pure_c = """
+#include "hello.h"
+
+int main() {
+    hello();
+}
+"""
+
+hello_c = """ #include <stdio.h>
+#include "hello.h"
+
+void hello() {
+    #ifdef NDEBUG
+        printf("Hello World Release!\\n");
+    #else
+        printf("Hello World Debug!\\n");
+    #endif
+}
+"""
+
 hello_h = """#pragma once
 
 #ifdef WIN32
@@ -222,6 +258,15 @@ def test(self):
 }
 """
 
+cmake_pure_c = """cmake_minimum_required(VERSION 2.8)
+project(MyHello C)
+
+include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+conan_basic_setup()
+
+add_library(hello hello.c)
+"""
+
 cmake = """cmake_minimum_required(VERSION 2.8)
 project(MyHello CXX)
 
@@ -293,8 +338,10 @@ def cmd_new(ref, header=False, pure_c=False, test=False, exports_sources=False,
 
     if header and exports_sources:
         raise ConanException("'header' and 'sources' are incompatible options")
-    if pure_c and (header or exports_sources):
-        raise ConanException("'pure_c' is incompatible with 'header' and 'sources'")
+    if pure_c and header:
+        raise ConanException("'pure_c' is incompatible with 'header'")
+    if pure_c and not exports_sources:
+        raise ConanException("'pure_c' requires the use of --source")
     if bare and (header or exports_sources):
         raise ConanException("'bare' is incompatible with 'header' and 'sources'")
     if template and (header or exports_sources or bare or pure_c):
@@ -304,12 +351,19 @@ def cmd_new(ref, header=False, pure_c=False, test=False, exports_sources=False,
     if header:
         files = {"conanfile.py": conanfile_header.format(name=name, version=version,
                                                          package_name=package_name)}
-    elif exports_sources:
+    elif exports_sources and not pure_c:
         files = {"conanfile.py": conanfile_sources.format(name=name, version=version,
                                                           package_name=package_name),
                  "src/hello.cpp": hello_cpp,
                  "src/hello.h": hello_h,
                  "src/CMakeLists.txt": cmake}
+
+    elif exports_sources and pure_c:
+        files = {"conanfile.py": conanfile_sources.format(name=name, version=version,
+                                                          package_name=package_name),
+                 "src/hello.c": hello_c,
+                 "src/hello.h": hello_h,
+                 "src/CMakeLists.txt": cmake_pure_c}
     elif bare:
         files = {"conanfile.py": conanfile_bare.format(name=name, version=version,
                                                        package_name=package_name)}
@@ -349,8 +403,12 @@ def cmd_new(ref, header=False, pure_c=False, test=False, exports_sources=False,
         files["test_package/conanfile.py"] = test_conanfile.format(name=name, version=version,
                                                                    user=user, channel=channel,
                                                                    package_name=package_name)
-        files["test_package/CMakeLists.txt"] = test_cmake
-        files["test_package/example.cpp"] = test_main
+        if pure_c:
+            files["test_package/example.c"] = test_main_pure_c
+            files["test_package/CMakeLists.txt"] = test_cmake_pure_c
+        else:
+            files["test_package/example.cpp"] = test_main
+            files["test_package/CMakeLists.txt"] = test_cmake
 
     if gitignore:
         files[".gitignore"] = gitignore_template


Test patch:
diff --git a/conans/test/functional/command/new_test.py b/conans/test/functional/command/new_test.py
--- a/conans/test/functional/command/new_test.py
+++ b/conans/test/functional/command/new_test.py
@@ -187,13 +187,12 @@ def new_sources_test(self):
 
     def new_purec_test(self):
         client = TestClient()
-        client.run('new MyPackage/1.3@myuser/testing -c -t')
+        client.run('new MyPackage/1.3@myuser/testing -c -t --source')
         root = client.current_folder
         self.assertTrue(os.path.exists(os.path.join(root, "conanfile.py")))
         content = load(os.path.join(root, "conanfile.py"))
         self.assertIn('name = "MyPackage"', content)
         self.assertIn('version = "1.3"', content)
-        self.assertIn('del self.settings.compiler.libcxx', content)
         self.assertIn('topics = (', content)
         self.assertNotIn('homepage', content)
         # assert they are correct at least