conan-io / conan

Conan - The open-source C and C++ package manager
https://conan.io
MIT License
8.24k stars 980 forks source link

[question] What is the replacement for `replace_path_in_file()` in Conan 2? #16779

Closed PauloCarvalhoRJ closed 2 weeks ago

PauloCarvalhoRJ commented 3 months ago

Hello,

It's a quite simple question. How do I replace replace_path_in_file() in Conan 2 recipes? It's purpose is to facilitate path replacement by ignoring letter case in the files. This is quite handy, especially in Windows.

thank you,

PC

Have you read the CONTRIBUTING guide?

czoido commented 2 months ago

Hi @PauloCarvalhoRJ,

Thanks for your question. I think we removed replace_path_in_file in Conan 2 because it was barely used (check https://github.com/conan-io/conan/issues/10502). Can you provide a bit more information about your use case? Is not something that you can do applying patches or by using replace_in_file?

PauloCarvalhoRJ commented 2 months ago

Hello,

As previously stated by me:

It's purpose is to facilitate path replacement by ignoring letter case in the files.

So, how do I disable case-sensitivity in replace_in_file?

Is not something that you can do applying patches ...?

Patches are also case sensitive.

thank you,

PC

czoido commented 2 months ago

Hi,

It's not possible to disable case-sensitivity in replace_in_file. Is that something you need to do very often when using Conan? Can you talk us a bit more about your use case? I would like to have more details in case we consider that it is something from which more users could benefit. For example, in the case of Conan Center Index, this need did not arise among more than 1000 recipes, which is why we removed it.

Thank you!

PauloCarvalhoRJ commented 2 months ago

Well, I think that's obvious. Windows paths are case-insensitive. C:\TMP is the same as c:\tmp and tens of all other possible combinations of lower- and upper-case letters. That's why it was in Conan 1. This issue is self-explanatory.

memsharded commented 2 months ago

I think the above request for feedback was about the root usage? Why is it necessary to replace a path in some file in the first place?

This was done in Conan 1 by the legacy CMake.patch_config_paths, but we learned that it was both fragile and not really a good practice, as it was incorrectly shifting the responsibility of creating correct xxx-config.cmake files. So the recommended approach is to avoid the replacement all together, for example by fixing the build scripts that generate those files. This was possible for +1500 recipes in ConanCenter, this is why the replace_path_in_file() was considered discouraged and removed in Conan 2 without a replacement.

memsharded commented 2 weeks ago

I'd like to update that there are also now new utilities like the new finalize() method that allows to do post-install customization of packages.

So I think that at the light that the whole ConanCenter has been able to move to Conan 2 without this utility, the above question would still be interesting to try to understand if there is a use case that we are missing:

Thanks for the feedback.

PauloCarvalhoRJ commented 2 weeks ago

The issue is not related to absolute paths.

Windows paths are case-insensitive. C:\TMP is the same as c:\tmp and tens of all other possible combinations of lower- and upper-case letters. That's the use case. I don't see any reason to over-elaborate this.

memsharded commented 2 weeks ago

Sure, paths in Windows are case-insensitive, we know it.

What we don't know is the use case, why it is necessary to replace paths in some files? The main reason this function existed in Conan 1 was because it was used by CMake.patch_config_paths(self):, and that method explains about:

For example, if a package foo installs a file called "fooConfig.cmake" to
        be used by cmake's find_package method, normally this file will contain
        absolute paths to the installed package folder, for example it will contain
        a line such as:

SET(Foo_INSTALL_DIR /home/developer/.conan/data/Foo/1.0.0/...)

But this is no longer true. Generated xxxConfig.cmake files shouldn't and won't have absolute paths, so no replace of paths is necessary anymore, and the full CMake.patch_config_paths(self): has been dropped too.

We know about the case-insensitivity, what we are trying to learn is the use case, the points above: who is generating that file, why the file contains paths that needs to be replaced, who is using those files, etc. For us it is very important to understand and learn about the "root" reasons, the use cases, the technologies and processes involved behind the functionality Conan provides, it is the only way to really provide useful tools, helpers and abstractions.

PauloCarvalhoRJ commented 2 weeks ago

I don't understand why you keep mentioning absolute paths... That was never the issue. But never mind. I'm going to implement a Python function to fulfill my need. Thank you for your time.

PauloCarvalhoRJ commented 2 weeks ago

Here's a snippet for anyone who may be looking for a replace_path_in_file() replacement:

import re

with open('foo.txt', 'r') as file:
    content = file.read()

pattern = re.compile('old_string', re.IGNORECASE)
content = pattern.sub('new_string', content)

with open('foo.txt', 'w') as file:
    file.write(content)

best,

PC

memsharded commented 2 weeks ago

I don't understand why you keep mentioning absolute paths... That was never the issue. But never mind. I'm going to implement a Python function to fulfill my need. Thank you for your time.

Because that was our only known use case in Conan 1 for us. It was documented and tested as such, as the docstring pasted above shows. If there was a use case to replace relative paths, we didn't know about it, and this is why we ask about the use case, it is good knowledge for us to be able to take informed decisions, not only about this specific replace_path_in_file(), but about other tools and processes.

In any case, thanks for sharing the above snippet. I think it might be even shorter leveraging load/save functions:

content = load(self, "foo.txt")
pattern = re.compile('old_string', re.IGNORECASE).sub('new_string', content)
save(self, "foo.txt", content)

Thanks for your feedback.

PauloCarvalhoRJ commented 1 week ago

Rereading your post, now I understand what you mean. Indeed a "replace path in file" use case is more often required when you have absolute paths in the files, which denotes a problem in the code. Thanks for optimizing the snippet. If I stumble upon some other generalizable functionality, I'll let you know.