Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-format

// libc++ supports basic_format_string in C++20 as an extension
// UNSUPPORTED: !stdlib=libc++ && c++20

// <format>

// template<class... Args>
// using format_string =
// basic_format_string<char, type_identity_t<Args>...>;
// template<class... Args>
// using wformat_string =
// basic_format_string<wchar_t, type_identity_t<Args>...>;

#include <format>

#include <concepts>

#include "test_macros.h"

static_assert(std::same_as<std::format_string<>, std::basic_format_string<char>>);
static_assert(std::same_as<std::format_string<int>, std::basic_format_string<char, int>>);
static_assert(std::same_as<std::format_string<int, float>, std::basic_format_string<char, int, float>>);
static_assert(std::same_as<std::format_string<int, float, void*>, std::basic_format_string<char, int, float, void*>>);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
static_assert(std::same_as<std::wformat_string<>, std::basic_format_string<wchar_t>>);
static_assert(std::same_as<std::wformat_string<int>, std::basic_format_string<wchar_t, int>>);
static_assert(std::same_as<std::wformat_string<int, float>, std::basic_format_string<wchar_t, int, float>>);
static_assert(
std::same_as<std::wformat_string<int, float, void*>, std::basic_format_string<wchar_t, int, float, void*>>);
#endif
5 changes: 5 additions & 0 deletions libcxx/utils/generate_feature_test_macro_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,11 @@ def add_version_header(tc):
"libcxx_guard": "!defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_filesystem)"
}, {
"name": "__cpp_lib_format",
# P2508, P2286, and P2419 were accepted in the same plenary and modify this
# feature-test macro. We expect to see an LWG issue soon. For now keep the
# value as is.
# TODO FMT Set P2508's feature-test macro.
#"values": { "c++20": 202106, "c++23": 202207" },
"values": { "c++20": 202106 },
"headers": ["format"],
"test_suite_guard": "!defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)",
Expand Down
4 changes: 2 additions & 2 deletions llvm/docs/CommandGuide/llvm-ranlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ llvm-ranlib - generates an archive index
SYNOPSIS
--------

:program:`llvm-ranlib` [*options*]
:program:`llvm-ranlib` [*options*] *archive...*

DESCRIPTION
-----------

:program:`llvm-ranlib` is an alias for the :doc:`llvm-ar <llvm-ar>` tool that
generates an index for an archive. It can be used as a replacement for GNU's
generates an index for one or more archives. It can be used as a replacement for GNU's
:program:`ranlib` tool.

Running :program:`llvm-ranlib` is equivalent to running ``llvm-ar s``.
Expand Down
6 changes: 4 additions & 2 deletions llvm/test/tools/llvm-ranlib/D-flag.test
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
# RUN: cp %t-no-index.a %t.a && llvm-ranlib -UUD %t.a
# RUN: env TZ=UTC llvm-ar tv %t.a | FileCheck %s --check-prefix=DETERMINISTIC-VALUES

## Check arguments can be passed before and after the file name
# RUN: cp %t-no-index.a %t.a && llvm-ranlib -U %t.a -D -U
## Check multiple archives can be specified and arguments can be specified anywhere.
# RUN: cp %t-no-index.a %t.a && cp %t-no-index.a %t2.a
# RUN: llvm-ranlib -U %t.a -D %t2.a -U
# RUN: env TZ=UTC llvm-ar tv %t.a | FileCheck %s --check-prefix=REAL-VALUES
# RUN: env TZ=UTC llvm-ar tv %t2.a | FileCheck %s --check-prefix=REAL-VALUES

## Check that the -D/-U option is only accepted with a single dash. This matches
## the GNU ranlib behaviour.
Expand Down
41 changes: 41 additions & 0 deletions llvm/test/tools/llvm-ranlib/error-opening-permission.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## Unsupported on windows as marking files "unreadable" is non-trivial on windows.
# UNSUPPORTED: system-windows

# RUN: rm -rf %t && split-file %s %t && cd %t
# RUN: yaml2obj 1.yaml -o 1.o
# RUN: llvm-ar rcS a.a 1.o
# RUN: cp a.a b.a && cp a.a c.a && cp a.a d.a
# RUN: chmod 100 c.a
# RUN: not llvm-ranlib a.a b.a c.a d.a 2>&1 | FileCheck %s --check-prefix=NO-PERMISSION -DMSG=%errc_EACCES

# NO-PERMISSION: error: unable to open 'c.a': [[MSG]]
# NO-PERMISSION-NOT: {{.}}

## The archives before c.a (a.a and b.a) have been processed.
# RUN: llvm-nm --print-armap a.a | FileCheck %s
# RUN: cmp a.a b.a
## The others (c.a and d.a) do not have a symbol table.
# RUN: chmod 700 c.a
# RUN: llvm-nm --print-armap c.a | FileCheck %s --check-prefix=NOMAP
# RUN: cmp c.a d.a

# CHECK: Archive map
# CHECK-NEXT: foo in 1.o
# CHECK-EMPTY:

# NOMAP-NOT: Archive map

#--- 1.yaml
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_REL
Machine: EM_X86_64
Sections:
- Name: .text
Type: SHT_PROGBITS
Symbols:
- Name: foo
Binding: STB_GLOBAL
Section: .text
33 changes: 16 additions & 17 deletions llvm/tools/llvm-ar/llvm-ar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ static StringRef ToolName;
static StringRef Stem;

static void printRanLibHelp(StringRef ToolName) {
outs() << "OVERVIEW: LLVM Ranlib\n\n"
<< "This program generates an index to speed access to archives\n\n"
<< "USAGE: " + ToolName + " <archive-file>\n\n"
outs() << "OVERVIEW: LLVM ranlib\n\n"
<< "Generate an index for archives\n\n"
<< "USAGE: " + ToolName + " archive...\n\n"
<< "OPTIONS:\n"
<< " -h --help - Display available options\n"
<< " -v --version - Display the version of this program\n"
Expand Down Expand Up @@ -1125,8 +1125,7 @@ static void performOperation(ArchiveOperation Operation,
llvm_unreachable("Unknown operation.");
}

static int performOperation(ArchiveOperation Operation,
std::vector<NewArchiveMember> *NewMembers) {
static int performOperation(ArchiveOperation Operation) {
// Create or open the archive object.
ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(
ArchiveName, /*IsText=*/false, /*RequiresNullTerminator=*/false);
Expand All @@ -1145,7 +1144,7 @@ static int performOperation(ArchiveOperation Operation,
if (Archive->isThin())
CompareFullPath = true;
performOperation(Operation, Archive.get(), std::move(Buf.get()),
NewMembers);
/*NewMembers=*/nullptr);
return 0;
}

Expand All @@ -1160,7 +1159,7 @@ static int performOperation(ArchiveOperation Operation,
}
}

performOperation(Operation, nullptr, nullptr, NewMembers);
performOperation(Operation, nullptr, nullptr, /*NewMembers=*/nullptr);
return 0;
}

Expand Down Expand Up @@ -1403,12 +1402,11 @@ static int ar_main(int argc, char **argv) {
Options += *ArgIt + 1;
}

ArchiveOperation Operation = parseCommandLine();
return performOperation(Operation, nullptr);
return performOperation(parseCommandLine());
}

static int ranlib_main(int argc, char **argv) {
bool ArchiveSpecified = false;
std::vector<StringRef> Archives;
for (int i = 1; i < argc; ++i) {
StringRef arg(argv[i]);
if (handleGenericOption(arg)) {
Expand All @@ -1433,16 +1431,17 @@ static int ranlib_main(int argc, char **argv) {
arg = arg.drop_front(1);
}
} else {
if (ArchiveSpecified)
fail("exactly one archive should be specified");
ArchiveSpecified = true;
ArchiveName = arg.str();
Archives.push_back(arg);
}
}
if (!ArchiveSpecified) {
badUsage("an archive name must be specified");

for (StringRef Archive : Archives) {
ArchiveName = Archive.str();
performOperation(CreateSymTab);
}
return performOperation(CreateSymTab, nullptr);
if (Archives.empty())
badUsage("an archive name must be specified");
return 0;
}

int llvm_ar_main(int argc, char **argv) {
Expand Down