Skip to content

Commit

Permalink
Add --output option to conan new command (#17359)
Browse files Browse the repository at this point in the history
* Add --output-folder option to conan new command

--------------------------

Co-authored-by: PerseoGI <perseog@jfrog.com>

* Add test for name being lowercase, fix it when no name was provided

---------

Co-authored-by: PerseoGI <perseog@jfrog.com>
  • Loading branch information
AbrilRBS and perseoGI authored Nov 21, 2024
1 parent b0e725b commit 6c620e8
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 16 deletions.
4 changes: 3 additions & 1 deletion conan/api/subapi/new.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,11 @@ def _read_files(self, template_folder):
@staticmethod
def render(template_files, definitions):
result = {}
name = definitions.get("name", "Pkg")
name = definitions.get("name", "pkg")
if isinstance(name, list):
raise ConanException(f"name argument can't be multiple: {name}")
if name != name.lower():
raise ConanException(f"name argument must be lowercase: {name}")
definitions["conan_version"] = __version__

def ensure_list(key):
Expand Down
8 changes: 5 additions & 3 deletions conan/cli/commands/new.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ def new(conan_api, parser, *args):
parser.add_argument("-d", "--define", action="append",
help="Define a template argument as key=value, e.g., -d name=mypkg")
parser.add_argument("-f", "--force", action='store_true', help="Overwrite file if it already exists")
parser.add_argument("-o", "--output", help="Output folder for the generated files",
default=os.getcwd())

args = parser.parse_args(*args)
# Manually parsing the remainder
Expand Down Expand Up @@ -59,18 +61,18 @@ def new(conan_api, parser, *args):

# Saving the resulting files
output = ConanOutput()
cwd = os.getcwd()
output_folder = args.output
# Making sure they don't overwrite existing files
for f, v in sorted(template_files.items()):
path = os.path.join(cwd, f)
path = os.path.join(output_folder, f)
if os.path.exists(path) and not args.force:
raise ConanException(f"File '{f}' already exists, and --force not defined, aborting")
save(path, v)
output.success("File saved: %s" % f)

# copy non-templates
for f, v in sorted(non_template_files.items()):
path = os.path.join(cwd, f)
path = os.path.join(output_folder, f)
if os.path.exists(path) and not args.force:
raise ConanException(f"File '{f}' already exists, and --force not defined, aborting")
shutil.copy2(v, path)
Expand Down
34 changes: 22 additions & 12 deletions test/integration/command/new_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

class TestNewCommand:
def test_new_cmake_lib(self):
client = TestClient()
client = TestClient(light=True)
client.run("new cmake_lib -d name=pkg -d version=1.3")
conanfile = client.load("conanfile.py")
assert "CMakeToolchain" in conanfile
Expand All @@ -25,7 +25,7 @@ def test_new_cmake_lib(self):
assert "pkg/1.3@myuser/testing" in client.out

def test_new_cmake_exe(self):
client = TestClient()
client = TestClient(light=True)
client.run("new cmake_exe -d name=pkg -d version=1.3")
conanfile = client.load("conanfile.py")
assert "CMakeToolchain" in conanfile
Expand All @@ -37,7 +37,7 @@ def test_new_cmake_exe(self):
assert "pkg/1.3@myuser/testing" in client.out

def test_new_basic_template(self):
tc = TestClient()
tc = TestClient(light=True)
tc.run("new basic")
assert '# self.requires("zlib/1.2.13")' in tc.load("conanfile.py")

Expand All @@ -48,7 +48,7 @@ def test_new_basic_template(self):
assert 'name = "mygame"' in conanfile

def test_new_defaults(self):
c = TestClient()
c = TestClient(light=True)
for t in ("cmake_lib", "cmake_exe", "meson_lib", "meson_exe", "msbuild_lib", "msbuild_exe",
"bazel_lib", "bazel_exe", "autotools_lib", "autotools_exe"):
c.run(f"new {t} -f")
Expand All @@ -66,7 +66,7 @@ class TestNewCommandUserTemplate:

@pytest.mark.parametrize("folder", ("mytemplate", "sub/mytemplate"))
def test_user_template(self, folder):
client = TestClient()
client = TestClient(light=True)
template1 = textwrap.dedent("""
class Conan(ConanFile):
name = "{{name}}"
Expand All @@ -83,7 +83,7 @@ class Conan(ConanFile):

def test_user_template_abs(self):
tmp_folder = temp_folder()
client = TestClient()
client = TestClient(light=True)
template1 = textwrap.dedent("""
class Conan(ConanFile):
name = "{{name}}"
Expand All @@ -94,13 +94,13 @@ class Conan(ConanFile):
assert 'name = "hello"' in conanfile

def test_user_template_filenames(self):
client = TestClient()
client = TestClient(light=True)
save(os.path.join(client.cache_folder, "templates/command/new/mytemplate/{{name}}"), "Hi!")
client.run(f"new mytemplate -d name=pkg.txt")
assert "Hi!" == client.load("pkg.txt")

def test_skip_files(self):
client = TestClient()
client = TestClient(light=True)
template1 = textwrap.dedent("""
class Conan(ConanFile):
name = "{{name}}"
Expand All @@ -121,7 +121,7 @@ class Conan(ConanFile):
def test_template_image_files(self):
""" problematic files that we dont want to render with Jinja, like PNG or other binaries,
have to be explicitly excluded from render"""
client = TestClient()
client = TestClient(light=True)
template_dir = "templates/command/new/t_dir"
png = "$&(){}{}{{}{}"
save(os.path.join(client.cache_folder, template_dir, "myimage.png"), png)
Expand All @@ -137,12 +137,12 @@ def test_template_image_files(self):

class TestNewErrors:
def test_template_errors(self):
client = TestClient()
client = TestClient(light=True)
client.run("new mytemplate", assert_error=True)
assert "ERROR: Template doesn't exist" in client.out

def test_forced(self):
client = TestClient()
client = TestClient(light=True)
client.run("new cmake_lib -d name=hello -d version=0.1")
client.run("new cmake_lib -d name=hello -d version=0.1", assert_error=True)
client.run("new cmake_lib -d name=bye -d version=0.2 --force")
Expand All @@ -151,10 +151,20 @@ def test_forced(self):
assert 'version = "0.2"' in conanfile

def test_duplicated(self):
client = TestClient()
client = TestClient(light=True)
client.run("new cmake_lib -d name=hello -d name=0.1", assert_error=True)
assert "ERROR: name argument can't be multiple: ['hello', '0.1']" in client.out

# It will create a list and assign to it, but it will not fail ugly
client.run("new cmake_lib -d name=pkg -d version=0.1 -d version=0.2", assert_error=True)
assert "ERROR: version argument can't be multiple: ['0.1', '0.2']" in client.out

def test_name_uppercase(self):
client = TestClient(light=True)
client.run("new cmake_lib -d name=Hello", assert_error=True)
assert "ERROR: name argument must be lowercase: Hello" in client.out

def test_new_change_folder(self):
client = TestClient(light=True)
client.run("new cmake_lib -d name=hello -d version=0.1 -o=myfolder")
assert os.path.exists(os.path.join(client.current_folder, "myfolder", "conanfile.py"))

0 comments on commit 6c620e8

Please sign in to comment.