1import os 2import re 3 4from jinja2 import Template 5 6from conans import __version__ as client_version 7from conans.client.cmd.new_ci import ci_get_files 8from conans.errors import ConanException 9from conans.model.ref import ConanFileReference, get_reference_fields 10from conans.util.files import load 11 12 13conanfile = """from conans import ConanFile, CMake, tools 14 15 16class {package_name}Conan(ConanFile): 17 name = "{name}" 18 version = "{version}" 19 license = "<Put the package license here>" 20 author = "<Put your name here> <And your email here>" 21 url = "<Package recipe repository url here, for issues about the package>" 22 description = "<Description of {package_name} here>" 23 topics = ("<Put some tag here>", "<here>", "<and here>") 24 settings = "os", "compiler", "build_type", "arch" 25 options = {{"shared": [True, False], "fPIC": [True, False]}} 26 default_options = {{"shared": False, "fPIC": True}} 27 generators = "cmake" 28 29 def config_options(self): 30 if self.settings.os == "Windows": 31 del self.options.fPIC 32 33 def source(self): 34 self.run("git clone https://github.com/conan-io/hello.git") 35 # This small hack might be useful to guarantee proper /MT /MD linkage 36 # in MSVC if the packaged project doesn't have variables to set it 37 # properly 38 tools.replace_in_file("hello/CMakeLists.txt", "PROJECT(HelloWorld)", 39 '''PROJECT(HelloWorld) 40include(${{CMAKE_BINARY_DIR}}/conanbuildinfo.cmake) 41conan_basic_setup()''') 42 43 def build(self): 44 cmake = CMake(self) 45 cmake.configure(source_folder="hello") 46 cmake.build() 47 48 # Explicit way: 49 # self.run('cmake %s/hello %s' 50 # % (self.source_folder, cmake.command_line)) 51 # self.run("cmake --build . %s" % cmake.build_config) 52 53 def package(self): 54 self.copy("*.h", dst="include", src="hello") 55 self.copy("*hello.lib", dst="lib", keep_path=False) 56 self.copy("*.dll", dst="bin", keep_path=False) 57 self.copy("*.so", dst="lib", keep_path=False) 58 self.copy("*.dylib", dst="lib", keep_path=False) 59 self.copy("*.a", dst="lib", keep_path=False) 60 61 def package_info(self): 62 self.cpp_info.libs = ["hello"] 63 64""" 65 66conanfile_bare = """from conans import ConanFile, tools 67 68 69class {package_name}Conan(ConanFile): 70 name = "{name}" 71 version = "{version}" 72 settings = "os", "compiler", "build_type", "arch" 73 description = "<Description of {package_name} here>" 74 url = "None" 75 license = "None" 76 author = "None" 77 topics = None 78 79 def package(self): 80 self.copy("*") 81 82 def package_info(self): 83 self.cpp_info.libs = tools.collect_libs(self) 84""" 85 86conanfile_sources = """from conans import ConanFile, CMake 87 88 89class {package_name}Conan(ConanFile): 90 name = "{name}" 91 version = "{version}" 92 license = "<Put the package license here>" 93 author = "<Put your name here> <And your email here>" 94 url = "<Package recipe repository url here, for issues about the package>" 95 description = "<Description of {package_name} here>" 96 topics = ("<Put some tag here>", "<here>", "<and here>") 97 settings = "os", "compiler", "build_type", "arch" 98 options = {{"shared": [True, False], "fPIC": [True, False]}} 99 default_options = {{"shared": False, "fPIC": True}} 100 generators = "cmake" 101 exports_sources = "src/*" 102{configure} 103 def config_options(self): 104 if self.settings.os == "Windows": 105 del self.options.fPIC 106 107 def build(self): 108 cmake = CMake(self) 109 cmake.configure(source_folder="src") 110 cmake.build() 111 112 # Explicit way: 113 # self.run('cmake %s/hello %s' 114 # % (self.source_folder, cmake.command_line)) 115 # self.run("cmake --build . %s" % cmake.build_config) 116 117 def package(self): 118 self.copy("*.h", dst="include", src="src") 119 self.copy("*.lib", dst="lib", keep_path=False) 120 self.copy("*.dll", dst="bin", keep_path=False) 121 self.copy("*.dylib*", dst="lib", keep_path=False) 122 self.copy("*.so", dst="lib", keep_path=False) 123 self.copy("*.a", dst="lib", keep_path=False) 124 125 def package_info(self): 126 self.cpp_info.libs = ["{name}"] 127""" 128 129conanfile_header = """import os 130 131from conans import ConanFile, tools 132 133 134class {package_name}Conan(ConanFile): 135 name = "{name}" 136 version = "{version}" 137 license = "<Put the package license here>" 138 author = "<Put your name here> <And your email here>" 139 url = "<Package recipe repository url here, for issues about the package>" 140 description = "<Description of {package_name} here>" 141 topics = ("<Put some tag here>", "<here>", "<and here>") 142 no_copy_source = True 143 # No settings/options are necessary, this is header only 144 145 def source(self): 146 '''retrieval of the source code here. Remember you can also put the code 147 in the folder and use exports instead of retrieving it with this 148 source() method 149 ''' 150 # self.run("git clone ...") or 151 # tools.download("url", "file.zip") 152 # tools.unzip("file.zip" ) 153 154 def package(self): 155 self.copy("*.h", "include") 156 157 def package_id(self): 158 self.info.header_only() 159""" 160 161 162test_conanfile = """import os 163 164from conans import ConanFile, CMake, tools 165 166 167class {package_name}TestConan(ConanFile): 168 settings = "os", "compiler", "build_type", "arch" 169 generators = "cmake" 170 171 def build(self): 172 cmake = CMake(self) 173 # Current dir is "test_package/build/<build_id>" and CMakeLists.txt is 174 # in "test_package" 175 cmake.configure() 176 cmake.build() 177 178 def imports(self): 179 self.copy("*.dll", dst="bin", src="bin") 180 self.copy("*.dylib*", dst="bin", src="lib") 181 self.copy('*.so*', dst='bin', src='lib') 182 183 def test(self): 184 if not tools.cross_building(self): 185 os.chdir("bin") 186 self.run(".%sexample" % os.sep) 187""" 188 189test_cmake = """cmake_minimum_required(VERSION 3.1) 190project(PackageTest CXX) 191 192include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 193conan_basic_setup() 194 195add_executable(example example.cpp) 196target_link_libraries(example ${CONAN_LIBS}) 197 198# CTest is a testing tool that can be used to test your project. 199# enable_testing() 200# add_test(NAME example 201# WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin 202# COMMAND example) 203""" 204 205test_cmake_pure_c = """cmake_minimum_required(VERSION 3.1) 206project(PackageTest C) 207 208include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 209conan_basic_setup() 210 211add_executable(example example.c) 212target_link_libraries(example ${CONAN_LIBS}) 213 214# CTest is a testing tool that can be used to test your project. 215# enable_testing() 216# add_test(NAME example 217# WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin 218# COMMAND example) 219""" 220 221test_main = """#include "{name}.h" 222 223int main() {{ 224 {name}(); 225}} 226""" 227 228hello_c = """ #include <stdio.h> 229#include "{name}.h" 230 231void {name}() {{ 232 int class = 0; //This will be an error in C++ 233 #ifdef NDEBUG 234 printf("{name}/{version}-(pure C): Hello World Release!\\n"); 235 #else 236 printf("{name}/{version}-(pure C): Hello World Debug!\\n"); 237 #endif 238}} 239""" 240 241hello_h = """#pragma once 242 243#ifdef WIN32 244 #define {name}_EXPORT __declspec(dllexport) 245#else 246 #define {name}_EXPORT 247#endif 248 249{name}_EXPORT void {name}(); 250""" 251 252hello_cpp = """#include <iostream> 253#include "{name}.h" 254 255void {name}(){{ 256 #ifdef NDEBUG 257 std::cout << "{name}/{version}: Hello World Release!" <<std::endl; 258 #else 259 std::cout << "{name}/{version}: Hello World Debug!" <<std::endl; 260 #endif 261}} 262""" 263 264cmake_pure_c = """cmake_minimum_required(VERSION 3.1) 265project({name} C) 266 267include(${{CMAKE_BINARY_DIR}}/conanbuildinfo.cmake) 268conan_basic_setup() 269 270add_library({name} {name}.c) 271""" 272 273cmake = """cmake_minimum_required(VERSION 3.1) 274project({name} CXX) 275 276include(${{CMAKE_BINARY_DIR}}/conanbuildinfo.cmake) 277conan_basic_setup() 278 279add_library({name} {name}.cpp) 280""" 281 282gitignore_template = """ 283*.pyc 284test_package/build 285 286""" 287 288 289def _render_template(text, name, version, package_name, defines): 290 context = {'name': name, 291 'version': version, 292 'package_name': package_name, 293 'conan_version': client_version} 294 context.update(defines) 295 t = Template(text, keep_trailing_newline=True) 296 return t.render(**context) 297 298 299def _get_files_from_template_dir(template_dir, name, version, package_name, defines): 300 files = [] 301 for d, _, fs in os.walk(template_dir): 302 for f in fs: 303 rel_d = os.path.relpath(d, template_dir) 304 rel_f = os.path.join(rel_d, f) 305 files.append(rel_f) 306 307 out_files = dict() 308 for f in files: 309 f_path = os.path.join(template_dir, f) 310 rendered_path = _render_template(f, name=name, version=version, package_name=package_name, 311 defines=defines) 312 rendered_file = _render_template(load(f_path), name=name, version=version, 313 package_name=package_name, defines=defines) 314 out_files[rendered_path] = rendered_file 315 316 return out_files 317 318 319def cmd_new(ref, header=False, pure_c=False, test=False, exports_sources=False, bare=False, 320 visual_versions=None, linux_gcc_versions=None, linux_clang_versions=None, 321 osx_clang_versions=None, shared=None, upload_url=None, gitignore=None, 322 gitlab_gcc_versions=None, gitlab_clang_versions=None, 323 circleci_gcc_versions=None, circleci_clang_versions=None, circleci_osx_versions=None, 324 template=None, cache=None, defines=None): 325 try: 326 name, version, user, channel, revision = get_reference_fields(ref, user_channel_input=False) 327 # convert "package_name" -> "PackageName" 328 package_name = re.sub(r"(?:^|[\W_])(\w)", lambda x: x.group(1).upper(), name) 329 except ValueError: 330 raise ConanException("Bad parameter, please use full package name," 331 "e.g.: MyLib/1.2.3@user/testing") 332 333 # Validate it is a valid reference 334 ConanFileReference(name, version, user, channel) 335 336 if header and exports_sources: 337 raise ConanException("'header' and 'sources' are incompatible options") 338 if pure_c and header: 339 raise ConanException("'pure_c' is incompatible with 'header'") 340 if pure_c and not exports_sources: 341 raise ConanException("'pure_c' requires the use of --source") 342 if bare and (header or exports_sources): 343 raise ConanException("'bare' is incompatible with 'header' and 'sources'") 344 if template and (header or exports_sources or bare or pure_c): 345 raise ConanException("'template' is incompatible with 'header', " 346 "'sources', 'pure-c' and 'bare'") 347 348 defines = defines or dict() 349 350 if header: 351 files = {"conanfile.py": conanfile_header.format(name=name, version=version, 352 package_name=package_name)} 353 elif exports_sources: 354 if not pure_c: 355 files = {"conanfile.py": conanfile_sources.format(name=name, version=version, 356 package_name=package_name, 357 configure=""), 358 "src/{}.cpp".format(name): hello_cpp.format(name=name, version=version), 359 "src/{}.h".format(name): hello_h.format(name=name, version=version), 360 "src/CMakeLists.txt": cmake.format(name=name, version=version)} 361 else: 362 config = ("\n def configure(self):\n" 363 " del self.settings.compiler.libcxx\n" 364 " del self.settings.compiler.cppstd\n") 365 files = {"conanfile.py": conanfile_sources.format(name=name, version=version, 366 package_name=package_name, 367 configure=config), 368 "src/{}.c".format(name): hello_c.format(name=name, version=version), 369 "src/{}.h".format(name): hello_h.format(name=name, version=version), 370 "src/CMakeLists.txt": cmake_pure_c.format(name=name, version=version)} 371 elif bare: 372 files = {"conanfile.py": conanfile_bare.format(name=name, version=version, 373 package_name=package_name)} 374 elif template: 375 is_file_template = os.path.basename(template).endswith('.py') 376 if is_file_template: 377 if not os.path.isabs(template): 378 # FIXME: Conan 2.0. The old path should be removed 379 old_path = os.path.join(cache.cache_folder, "templates", template) 380 new_path = os.path.join(cache.cache_folder, "templates", "command/new", template) 381 template = new_path if os.path.isfile(new_path) else old_path 382 if not os.path.isfile(template): 383 raise ConanException("Template doesn't exist: %s" % template) 384 replaced = _render_template(load(template), 385 name=name, 386 version=version, 387 package_name=package_name, 388 defines=defines) 389 files = {"conanfile.py": replaced} 390 elif template == "cmake_lib": 391 from conans.assets.templates.new_v2_cmake import get_cmake_lib_files 392 files = get_cmake_lib_files(name, version, package_name) 393 elif template == "cmake_exe": 394 from conans.assets.templates.new_v2_cmake import get_cmake_exe_files 395 files = get_cmake_exe_files(name, version, package_name) 396 else: 397 if not os.path.isabs(template): 398 template = os.path.join(cache.cache_folder, "templates", "command/new", template) 399 if not os.path.isdir(template): 400 raise ConanException("Template doesn't exist: {}".format(template)) 401 template = os.path.normpath(template) 402 files = _get_files_from_template_dir(template_dir=template, 403 name=name, 404 version=version, 405 package_name=package_name, 406 defines=defines) 407 else: 408 files = {"conanfile.py": conanfile.format(name=name, version=version, 409 package_name=package_name)} 410 411 if test: 412 files["test_package/conanfile.py"] = test_conanfile.format(name=name, version=version, 413 user=user, channel=channel, 414 package_name=package_name) 415 if pure_c: 416 files["test_package/example.c"] = test_main.format(name=name) 417 files["test_package/CMakeLists.txt"] = test_cmake_pure_c 418 else: 419 include_name = name if exports_sources else "hello" 420 files["test_package/example.cpp"] = test_main.format(name=include_name) 421 files["test_package/CMakeLists.txt"] = test_cmake 422 423 if gitignore: 424 files[".gitignore"] = gitignore_template 425 426 files.update(ci_get_files(name, version, user, channel, visual_versions, 427 linux_gcc_versions, linux_clang_versions, 428 osx_clang_versions, shared, upload_url, 429 gitlab_gcc_versions, gitlab_clang_versions, 430 circleci_gcc_versions, circleci_clang_versions, 431 circleci_osx_versions)) 432 return files 433